PT-2026-65650 · Pypi · Datamodel-Code-Generator
Published
2026-07-28
·
Updated
2026-07-28
CVSS v3.1
7.8
High
| Vector | AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H |
Summary
When the Pydantic v2 output mode is in use,
datamodel-code-generator reads a validators array from each model entry in the --extra-template-data file and synthesises a Pydantic @field validator(...) decorator from each entry. The field names and the validator mode are interpolated into the decorator call wrapped in unescaped single quotes. A value containing ' breaks out of the string literal, letting an attacker emit an arbitrary positional Python expression into the decorator. The expression is evaluated at class-definition time, i.e. the moment the developer imports the generated module. This is the same trust model as the recently-published GHSA-wjv6-jcfj-mf9r (extras-file comment injection) but the impact is full RCE rather than a docstring leak.Details
Sink:
src/datamodel code generator/model/pydantic v2/base model.py, process validators (lines 405–449, at tag 0.60.1 / commit a321547e):python
def process validators(self) -> None:
validators = self.extra template data.get("validators")
if not validators:
return
...
for validator in validators:
fields = validator.get("fields") or [validator.get("field")]
fields = [f for f in fields if f]
if not fields:
continue
function path: str = validator["function"]
function name = function path.rsplit(".", 1)[-1]
mode = validator.get("mode", "after")
fields str = ", ".join(f"'{f}'" for f in fields) # (A) UNESCAPED
...
mode str = f"mode='{mode}'" # (B) UNESCAPED
prepared validators.append({
"fields str": fields str,
"mode str": mode str,
"method name": method name,
"function name": function name,
"mode": mode,
})
self. additional imports.append(Import.from full path(function path)) # (C)The strings from (A) and (B) flow verbatim into
src/datamodel code generator/model/template/pydantic v2/BaseModel.jinja2:jinja
@field validator({{ v.fields str }}, {{ v.mode str }})There is no
repr() call, no identifier check, and no quote-escaping.Secondary sink at (C):
Import.from full path(function path) splits on the last . and emits from <prefix> import <suffix>. A ; in function path therefore lands in the generated import line and runs as a statement at module load.PoC
A self-contained one-file PoC is available here: https://gist.github.com/thegr1ffyn/34d5c647e74487ffb2be27c76dace2aa
Impact
Arbitrary code execution in the developer's interpreter / CI runner the moment the generated module is imported. Anyone who accepts a
--extra-template-data file from an untrusted source is impacted:- Pull requests adding or modifying project-local
*.template-data.json/.codegen.jsonfiles consumed by amake codegenrule or pre-commit hook. - Configuration snippets pasted from issue templates, READMEs, or third-party guides.
- Multi-tenant CI systems where one tenant's config file is read by another tenant's build.
Same blast radius as GHSA-wjv6-jcfj-mf9r, but silent RCE rather than a docstring leak — significantly higher impact under the same threat model.
Introduced in 0.52.1 by commita2b27562(Add --validators option for Pydantic v2 field validators).
Resolution
The fix validates
validators entries with Pydantic models before rendering them. Field names must be valid non-keyword Python identifiers, function must be a dotted Python identifier path, and mode must be one of Pydantic's supported validator modes. The generated decorator arguments now render field names with repr() and mode with !r, so validated values are still emitted as Python string literals.Remediation
Upgrade to
datamodel-code-generator 0.60.2 or later.This issue affects
datamodel-code-generator versions >= 0.52.1, <= 0.60.1 and is fixed in 0.60.2.Submitted by: Hamza Haroon (thegr1ffyn)
Fix
Code Injection
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Datamodel-Code-Generator