VYPR
High severityNVD Advisory· Published Apr 26, 2022· Updated Apr 22, 2025

Command Injection in Ballcat Codegen

CVE-2022-24881

Description

Ballcat Codegen provides the function of online editing code to generate templates. In versions prior to 1.0.0.beta.2, attackers can implement remote code execution through malicious code injection of the template engine. This happens because Velocity and freemarker templates are introduced but input verification is not done. The fault is rectified in version 1.0.0.beta.2.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Ballcat Codegen before 1.0.0.beta.2 allows remote code execution via template injection in Velocity and Freemarker engines due to missing input validation.

Vulnerability

Ballcat Codegen provides online code editing with template generation using Velocity and Freemarker engines. In versions prior to 1.0.0.beta.2, the template engine does not validate user-supplied template content, allowing injection of malicious code that executes during template rendering. The issue is documented in [1] and [4].

Exploitation

An attacker with access to the template editing functionality can inject malicious code into a template. The attacker does not require authentication if the application is exposed, but typically a user with permissions to edit templates can perform the attack. By crafting a template containing Velocity or Freemarker directives, the attacker can trigger execution of arbitrary code on the server when the template is rendered.

Impact

Successful exploitation results in remote code execution (RCE) on the server hosting the code generator. The attacker gains the ability to execute arbitrary commands, potentially leading to full compromise of the application and underlying system.

Mitigation

The vulnerability is fixed in version 1.0.0.beta.2, released on or before April 26, 2022. The fix adds a safer class resolver (TemplateClassResolver.SAFER_RESOLVER) to the Freemarker configuration and throws exceptions on template errors [2]. Users should upgrade to 1.0.0.beta.2 or later. No workaround is documented.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
com.hccake:ballcat-codegenMaven
< 1.0.0.beta.21.0.0.beta.2

Affected products

3

Patches

1
84a7cb38daf0

:lock: 修改模板引擎的默认安全策略,以防止RCE

6 files changed · +34 10
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/engine/FreemarkerTemplateEngine.java+4 0 modified
    @@ -1,6 +1,7 @@
     package com.hccake.ballcat.codegen.engine;
     
     import com.hccake.ballcat.codegen.exception.TemplateRenderException;
    +import freemarker.core.TemplateClassResolver;
     import freemarker.template.Configuration;
     import freemarker.template.Template;
     import lombok.SneakyThrows;
    @@ -23,6 +24,9 @@ public class FreemarkerTemplateEngine implements TemplateEngine {
     	public FreemarkerTemplateEngine() {
     		this.configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
     		configuration.setDefaultEncoding(StandardCharsets.UTF_8.name());
    +
    +		// 安全处理 https://ackcent.com/blog/in-depth-freemarker-template-injection/
    +		configuration.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
     	}
     
     	@Override
    
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/engine/TemplateEngineDelegator.java+4 2 modified
    @@ -2,12 +2,13 @@
     
     import cn.hutool.core.lang.Assert;
     import cn.hutool.core.util.StrUtil;
    +import com.hccake.ballcat.codegen.exception.TemplateRenderException;
     
     import java.util.Map;
     
     /**
      * 模板引擎的委托者
    - * 
    + *
      * @author hccake
      */
     public class TemplateEngineDelegator {
    @@ -24,7 +25,8 @@ public TemplateEngineDelegator(Map<TemplateEngineTypeEnum, TemplateEngine> templ
     	 * @param context 渲染使用的上下文
     	 * @return 渲染完成后的字符串
     	 */
    -	public String render(TemplateEngineTypeEnum engineType, String templateContent, Map<String, Object> context) {
    +	public String render(TemplateEngineTypeEnum engineType, String templateContent, Map<String, Object> context)
    +			throws TemplateRenderException {
     		if (StrUtil.isEmpty(templateContent)) {
     			return StrUtil.EMPTY;
     		}
    
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/engine/TemplateEngine.java+3 1 modified
    @@ -1,5 +1,7 @@
     package com.hccake.ballcat.codegen.engine;
     
    +import com.hccake.ballcat.codegen.exception.TemplateRenderException;
    +
     import java.util.Map;
     
     /**
    @@ -21,6 +23,6 @@ public interface TemplateEngine {
     	 * @param context 渲染使用的上下文
     	 * @return 渲染完成后的字符串
     	 */
    -	String render(String templateContent, Map<String, Object> context);
    +	String render(String templateContent, Map<String, Object> context) throws TemplateRenderException;
     
     }
    
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/engine/VelocityTemplateEngine.java+4 1 modified
    @@ -4,6 +4,7 @@
     import org.apache.velocity.VelocityContext;
     import org.apache.velocity.app.Velocity;
     import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
    +import org.apache.velocity.util.introspection.SecureUberspector;
     import org.springframework.stereotype.Component;
     
     import java.io.StringWriter;
    @@ -22,6 +23,8 @@ public class VelocityTemplateEngine implements TemplateEngine {
     		// 设置velocity资源加载器, 保留文件加载loader
     		Properties prop = new Properties();
     		prop.put("file.resource.loader.class", ClasspathResourceLoader.class.getName());
    +		// 安全处理
    +		prop.put("runtime.introspector.uberspect", SecureUberspector.class.getName());
     		Velocity.init(prop);
     	}
     
    @@ -31,7 +34,7 @@ public TemplateEngineTypeEnum type() {
     	}
     
     	@Override
    -	public String render(String templateContent, Map<String, Object> context) {
    +	public String render(String templateContent, Map<String, Object> context) throws TemplateRenderException {
     		VelocityContext velocityContext = new VelocityContext(context);
     		try (StringWriter sw = new StringWriter()) {
     			Velocity.evaluate(velocityContext, sw, "velocityTemplateEngine", templateContent);
    
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/exception/TemplateRenderException.java+1 1 modified
    @@ -5,7 +5,7 @@
      *
      * @author hccake
      */
    -public class TemplateRenderException extends RuntimeException {
    +public class TemplateRenderException extends Exception {
     
     	public TemplateRenderException(Exception e) {
     		super(e);
    
  • ballcat-codegen-backend/src/main/java/com/hccake/ballcat/codegen/service/impl/GeneratorServiceImpl.java+18 5 modified
    @@ -6,6 +6,7 @@
     import com.hccake.ballcat.codegen.constant.TemplateEntryTypeEnum;
     import com.hccake.ballcat.codegen.engine.TemplateEngineDelegator;
     import com.hccake.ballcat.codegen.engine.TemplateEngineTypeEnum;
    +import com.hccake.ballcat.codegen.exception.TemplateRenderException;
     import com.hccake.ballcat.codegen.model.bo.FileEntry;
     import com.hccake.ballcat.codegen.model.bo.TableDetails;
     import com.hccake.ballcat.codegen.model.bo.TemplateFile;
    @@ -15,6 +16,8 @@
     import com.hccake.ballcat.codegen.service.TableInfoQuery;
     import com.hccake.ballcat.codegen.service.TemplateEntryService;
     import com.hccake.ballcat.codegen.util.GenUtils;
    +import com.hccake.ballcat.common.core.exception.BusinessException;
    +import com.hccake.ballcat.common.model.result.SystemResultCode;
     import lombok.RequiredArgsConstructor;
     import lombok.extern.slf4j.Slf4j;
     import org.springframework.stereotype.Service;
    @@ -133,22 +136,32 @@ public Map<String, FileEntry> generatorCode(TableDetails tableDetails, String ta
     			fileEntry.setType(templateFile.getType());
     
     			// 替换路径中的占位符
    -			String filename = StrUtil.format(templateFile.getFilename(), context);
    +			String templateFilename = templateFile.getFilename();
    +			String filename = StrUtil.format(templateFilename, context);
     			fileEntry.setFilename(filename);
     
     			String parentFilePath = GenUtils.evaluateRealPath(templateFile.getParentFilePath(), context);
     			fileEntry.setParentFilePath(parentFilePath);
     
     			// 如果是文件
     			if (TemplateEntryTypeEnum.FILE.getType().equals(fileEntry.getType())) {
    -				fileEntry.setFilePath(GenUtils.concatFilePath(parentFilePath, filename));
    +				String filePath = GenUtils.concatFilePath(parentFilePath, filename);
    +				fileEntry.setFilePath(filePath);
     				// 文件内容渲染
     				TemplateEngineTypeEnum engineTypeEnum = TemplateEngineTypeEnum.of(templateFile.getEngineType());
    -				String content = templateEngineDelegator.render(engineTypeEnum, templateFile.getContent(), context);
    -				fileEntry.setContent(content);
    +
    +				try {
    +					String content = templateEngineDelegator.render(engineTypeEnum, templateFile.getContent(), context);
    +					fileEntry.setContent(content);
    +				}
    +				catch (TemplateRenderException ex) {
    +					String errorMessage = StrUtil.format("模板渲染异常,模板文件名:【{}】,错误详情:{}", templateFilename,
    +							ex.getMessage());
    +					throw new BusinessException(SystemResultCode.SERVER_ERROR.getCode(), errorMessage);
    +				}
     			}
     			else {
    -				String currentPath = GenUtils.evaluateRealPath(templateFile.getFilename(), context);
    +				String currentPath = GenUtils.evaluateRealPath(templateFilename, context);
     				fileEntry.setFilePath(GenUtils.concatFilePath(parentFilePath, currentPath));
     			}
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.