← Back to results
Vulnerability record · public intelligence
CVE-2026-55415
HIGH

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

Published Jul 28, 2026 · Updated Nov 3, 2026

Severity · CVSSHIGH · Not scored
Known exploitationNot listed
EPSS / likelihood0.49%
QFinch risk scoreNot available

Original summary

Normalized public record · Updated Nov 3, 2026

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

Publisher and source text is retained separately from QFinch interpretation.

QFinch assessment

A malicious input schema (OpenAPI / JSON Schema) can execute arbitrary Python code on the machine that imports the generated model. The x-python-import and customTypePath schema extensions flow, unsanitized, into the import statements datamodel-code-generator emits. A newline embedded in the extension value breaks out of the from … import … line and injects an attacker-controlled statement at module scope, which runs at import time. This is an unauthenticated, schema-content–driven remote code execution against any consumer of the generated code (e.g. arbitrary file read,the PoC exfiltrates /etc/passwd). It survives the v0.61.0 security release that fixed the related x-python-type, defaultfactory, GraphQL-union-description, and validators sinks those fixes did not cover this sibling path. Details The sink is Import.fromfullpath and Imports.createline: - src/datamodelcodegenerator/imports.py:35 — fromfullpath() only does classpath.split(".") and preserves every other character, including newlines: - src/datamodelcodegenerator/imports.py:64 — createline() renders the result verbatim: Two schema-controlled, default-config entry points reach this sink: 1. x-python-import — src/datamodelcodegenerator/parser/jsonschema.py:1851-1858 (getrefdatatype): Mechanism. With name = "getcwd\nprint(...)", fullpath = "os.getcwd\nprint(...)". fromfullpath splits only on ., so (provided the injected statement contains no .) it yields from="os" and import="getcwd\nprint(...)". createline then emits: The dot-split is the only constraint on the payload; it is trivially satisfied with attribute-free builtins (e.g. print(open('/etc/passwd'), file=open('/tmp/loot','w'), sep='', end='') reads and exfiltrates a file using no .). None of the six v0.61.0 fix commits (aec47bc4, b73abb5c, 17fc235e, 2c93c9b7, a43d0290, 5fdba4a0) touched x-python-import, customTypePath, or imports.py; imports.py was last modified ~5 months before the release. This is therefore an incomplete fix: the maintainer hardened sibling schema-controlled type/extension sinks but missed these two paths into the same import-generation code. PoC Self contained POC available here: https://gist.github.com/thegr1ffyn/c3abb41bb89c164daa0d5f2c60b5328b Default invocation, no special flags, on the patched release (commit 227ffe85ee2dcfc79336fbb14ad64c02a166b65a, v0.61.0). payloada.json: Generate and import: Generated model.py (verbatim, the breakout sits at module scope): Importing model reads /etc/passwd and writes an exact copy to /tmp/dmcgxpiloot: Confirmed under both the default output (pydantic v1) and --output-model-type pydanticv2.BaseModel. The customTypePath variant reproduces identically: A benign control (x-python-import: {"module":"decimal","name":"Decimal"}) produces clean from decimal import Decimal and no execution. A complete self-contained validation harness, run.sh plus control.json, payloada.json, payloadb.json, is included alongside this advisory (CONTROL clean + three payloads firing + verdict + cleanup). Suggested fix. Validate every dotted segment of module, name, and customTypePath as a Python identifier before building the import (reuse validators.validatedottedpythonidentifierpath), and/or reject non-identifier paths centrally inside Import.fromfullpath. Impact Arbitrary code execution at model-import time, driven by attacker-controlled schema content under the default configuration. Anyone who runs datamodel-code-generator on an untrusted or third-party schema, multi-tenant code-generation services, CI pipelines that ingest external specs, or a developer generating models from a public/vendor OpenAPI/JSON-Schema document, and then imports (or whose tooling imports) the generated module, executes the attacker's code with the importing process's privileges. The PoC demonstrates arbitrary local file read (/etc/passwd); the same primitive yields full RCE.

Potential impact

Arbitrary code execution at model-import time, driven by attacker-controlled schema content under the default configuration. Anyone who runs datamodel-code-generator on an untrusted or third-party schema, multi-tenant code-generation services, CI pipelines that ingest external specs, or a developer generating models from a public/vendor OpenAPI/JSON-Schema document, and then imports (or whose tooling imports) the generated module, executes the attacker's code with the importing process's privileges. The PoC demonstrates arbitrary local file read (/etc/passwd); the same primitive yields full RCE.

Technical context

The sink is Import.fromfullpath and Imports.createline: - src/datamodelcodegenerator/imports.py:35 — fromfullpath() only does classpath.split(".") and preserves every other character, including newlines: - src/datamodelcodegenerator/imports.py:64 — createline() renders the result verbatim: Two schema-controlled, default-config entry points reach this sink: 1. x-python-import — src/datamodelcodegenerator/parser/jsonschema.py:1851-1858 (getrefdatatype): Mechanism. With name = "getcwd\nprint(...)", fullpath = "os.getcwd\nprint(...)". fromfullpath splits only on ., so (provided the injected statement contains no .) it yields from="os" and import="getcwd\nprint(...)". createline then emits: The dot-split is the only constraint on the payload; it is trivially satisfied with attribute-free builtins (e.g. print(open('/etc/passwd'), file=open('/tmp/loot','w'), sep='', end='') reads and exfiltrates a file using no .). None of the six v0.61.0 fix commits (aec47bc4, b73abb5c, 17fc235e, 2c93c9b7, a43d0290, 5fdba4a0) touched x-python-import, customTypePath, or imports.py; imports.py was last modified ~5 months before the release. This is therefore an incomplete fix: the maintainer hardened sibling schema-controlled type/extension sinks but missed these two paths into the same import-generation code. PoC Self contained POC available here: https://gist.github.com/thegr1ffyn/c3abb41bb89c164daa0d5f2c60b5328b Default invocation, no special flags, on the patched release (commit 227ffe85ee2dcfc79336fbb14ad64c02a166b65a, v0.61.0). payloada.json: Generate and import: Generated model.py (verbatim, the breakout sits at module scope): Importing model reads /etc/passwd and writes an exact copy to /tmp/dmcgxpiloot: Confirmed under both the default output (pydantic v1) and --output-model-type pydanticv2.BaseModel. The customTypePath variant reproduces identically: A benign control (x-python-import: {"module":"decimal","name":"Decimal"}) produces clean from decimal import Decimal and no execution. A complete self-contained validation harness, run.sh plus control.json, payloada.json, payloadb.json, is included alongside this advisory (CONTROL clean + three payloads firing + verdict + cleanup). Suggested fix. Validate every dotted segment of module, name, and customTypePath as a Python identifier before building the import (reuse validators.validatedottedpythonidentifierpath), and/or reject non-identifier paths centrally inside Import.fromfullpath. Impact Arbitrary code execution at model-import time, driven by attacker-controlled schema content under the default configuration. Anyone who runs datamodel-code-generator on an untrusted or third-party schema, multi-tenant code-generation services, CI pipelines that ingest external specs, or a developer generating models from a public/vendor OpenAPI/JSON-Schema document, and then imports (or whose tooling imports) the generated module, executes the attacker's code with the importing process's privileges. The PoC demonstrates arbitrary local file read (/etc/passwd); the same primitive yields full RCE.

Evidence: public source records

Affected products

Affected scope reported by the available public evidence
Affected package, product or rangeFix status
Consult the referenced vendor advisory for affected versions.Consult source advisory

Matching a version is only one part of assessing exposure. QFinch has not assessed your environment on this public page.

Remediation

Apply the vendor-supported fixed release or mitigation, then verify the affected component is no longer reachable.

  1. Confirm the affected product and reachable code path exist in your environment.
  2. Apply the vendor-supported patch or mitigation from the primary advisory.
  3. Rescan the exact deployed revision and retain verification evidence.
Review primary references

Evidence coverage

FIRST-EPSSCVE-LIST-V5NVD-MODIFIED
SSVC priority
MEDIUM
Confidence
Not available
Aliases
None reported

Scores appear only when corresponding evidence exists. Missing data is never interpreted as zero risk.

Sources and record history

  1. Patch commit ↗
  2. Source repository ↗
  3. Security advisory ↗

Record history

Published Jul 28, 2026 · Last refreshed Nov 3, 2026