Command Injection in Ballcat Codegen
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.
| Package | Affected versions | Patched versions |
|---|---|---|
com.hccake:ballcat-codegenMaven | < 1.0.0.beta.2 | 1.0.0.beta.2 |
Affected products
3<1.0.0.beta.2+ 1 more
- (no CPE)range: <1.0.0.beta.2
- (no CPE)range: < 1.0.0.beta.2
Patches
184a7cb38daf0: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- github.com/advisories/GHSA-fv3m-xhqw-9m79ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-24881ghsaADVISORY
- github.com/ballcat-projects/ballcat-codegen/commit/84a7cb38daf0295b93aba21d562ec627e4eb463bghsax_refsource_MISCWEB
- github.com/ballcat-projects/ballcat-codegen/issues/5ghsax_refsource_MISCWEB
- github.com/ballcat-projects/ballcat-codegen/security/advisories/GHSA-fv3m-xhqw-9m79ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.