{"id":"CVE-2026-54654","aliases":["GHSA-wjv6-jcfj-mf9r","PYSEC-2026-3566"],"title":"`datamodel-code-generator` vulnerable to code injection via unescaped carriage return in `--extra-template-data` `comment` field","summary":"`datamodel-code-generator` vulnerable to code injection via unescaped carriage return in `--extra-template-data` `comment` field","severity":"high","cvss":7.8,"cvssVector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","vendor":"datamodel-code-generator","product":"datamodel-code-generator","ecosystem":"pip","affected":["datamodel-code-generator >= 0.14.1, < 0.60.2"],"patched":["datamodel-code-generator 0.60.2"],"published":"2026-07-28","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:51:12.477383004Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-wjv6-jcfj-mf9r","references":[{"url":"https://github.com/koxudaxi/datamodel-code-generator/security/advisories/GHSA-wjv6-jcfj-mf9r"},{"url":"https://github.com/koxudaxi/datamodel-code-generator/commit/b73abb5cd703a50471b8950bbd3bd0b82ad71de7"},{"url":"https://github.com/koxudaxi/datamodel-code-generator"},{"url":"https://github.com/koxudaxi/datamodel-code-generator/releases/tag/0.60.2"},{"url":"https://github.com/advisories/GHSA-wjv6-jcfj-mf9r"}],"tags":["osv","pip","ghsa"],"epss":0.0021,"epssPercentile":0.10044,"cwe":["CWE-94","CWE-1336"],"ingestedAt":"2026-07-28T22:40:03.394Z","slug":"CVE-2026-54654","body":"## Overview\n\n### Summary\n\n`datamodel-code-generator` is vulnerable to code injection when a developer passes an `--extra-template-data` file whose `comment` value contains a literal `\\r` (carriage return). The `comment` variable is rendered into a Python `#` comment in six built-in templates with **no** line-terminator escaping. Python's tokenizer treats a bare CR as a physical-line terminator (see [Python language reference — Physical lines](https://docs.python.org/3/reference/lexical_analysis.html#physical-lines)), so the comment ends at the `\\r` and the text after it is parsed as Python, including, when the CR is followed by suitable indentation, as a statement within the class body that follows on the next template line.\n\n### Details\n\nThe vulnerable templates each contain `# {{ comment }}` with no escaping:\n\n- `src/datamodel_code_generator/model/template/TypeAliasAnnotation.jinja2:12` and `:19`\n- `src/datamodel_code_generator/model/template/TypeAliasType.jinja2:12` and `:19`\n- `src/datamodel_code_generator/model/template/TypeStatement.jinja2:12` and `:19`\n- `src/datamodel_code_generator/model/template/pydantic_v2/BaseModel.jinja2:4`\n- `src/datamodel_code_generator/model/template/pydantic_v2/RootModel.jinja2:19`\n- `src/datamodel_code_generator/model/template/pydantic_v2/RootModelTypeAlias.jinja2:13`\n\nThe `pydantic_v2/BaseModel.jinja2:4` site is representative:\n\n```jinja2\nclass {{ class_name }}({{ base_class }}):{% if comment is defined %}  # {{ comment }}{% endif %}\n```\n\nWhen the developer-supplied extras file populates `comment` for a model, the value reaches the template via `DataModel.extra_template_data` (set in `src/datamodel_code_generator/model/base.py:736-742`) and Jinja2 interpolates it raw. None of the templates use `comment_safe`, `escape_docstring`, or any other line-terminator filter.\n\n### PoC\nComplete self contained POC is available at my secret gist: https://gist.github.com/thegr1ffyn/8ad6b8cb3cc2be9d3a0144aeb6896a3f\n\n### Impact\n\n- **Who's affected**: any developer or CI pipeline that runs `datamodel-codegen --extra-template-data <file>` where the extras file is influenced by attacker-controlled input. Realistic scenarios include:\n  - Extras file generated from a third-party schema-annotation system.\n  - Extras file vendored from an upstream repository.\n  - Extras file produced by a script that merges multiple `comment` sources.\n  - Build pipelines that template the extras file from environment variables, ticket descriptions, or commit metadata.\n- **What it gains**: arbitrary Python code execution in the importer's process at `import` time.\n- **What it does NOT need**: the schema itself can be entirely benign; only the extras file needs to contain the malicious `comment`.\n- **What does block it**: not passing `--extra-template-data`, or rejecting extras files whose `comment` values contain `\\r`, `\\x0b`, or `\\x0c` before invocation.\n\n### Resolution\n\nThe fix normalizes `comment` values from built-in `--extra-template-data` before template rendering. Inline comments now convert CRLF, bare CR, vertical tab, and form feed into LF and prefix continuation lines with `# `, so attacker-controlled text stays inside the generated Python comment block.\n\n### Remediation\n\nUpgrade to `datamodel-code-generator` `0.60.2` or later.\n\nThis issue affects `datamodel-code-generator` versions `>= 0.14.1, <= 0.60.1` and is fixed in `0.60.2`.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)\n\n## Affected packages\n\n- `datamodel-code-generator >= 0.14.1, < 0.60.2`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `datamodel-code-generator 0.60.2`","depth":"twilight","depthScore":43,"depthScoreParts":{"impact":42.9,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}