Описание
datamodel-code-generator vulnerable to arbitrary local file read via JSON-Schema $ref (file:// and ../ traversal), bypassing --no-allow-remote-refs
Summary
datamodel-code-generator resolves JSON-Schema $ref targets that point at the local filesystem without restricting them to the input/base directory and without honoring the remote-reference security control. In the default configuration, an attacker who controls an input schema (a "paste your OpenAPI/JSON-Schema" service, a CI job that generates models from a submitted spec, or any multi-tenant codegen platform) can read any file the process user can read and map the host filesystem. This works via either a file:// absolute URI or a ../-escaped relative reference, and it succeeds even when --no-allow-remote-refs is set.
This is an unauthenticated path-traversal / information-disclosure issue (CWE-22 / CWE-200) plus a bypass of a documented security control.
Details
is_url() classifies file:// as a URL (reference.py:1249):
The remote-ref gate then explicitly exempts file://, so --no-allow-remote-refs never applies to it (parser/jsonschema.py, _get_ref_body):
Both local-file branches read the target with no containment check. The file:// branch reads any absolute path:
and the plain relative branch lets ../ escape the base directory:
For contrast, the HTTP-local-ref branch (_get_ref_body_from_local_http_path) does enforce is_relative_to(base_path); these two filesystem branches do not. (This is distinct from the previously reported HTTP $ref/--url SSRF hardened in http.py these code paths never enter http.py.)
The fetched file is read and parsed, yielding two impacts:
-
Arbitrary file read + filesystem oracle (any file). The process opens any readable path, and the three distinguishable outcomes leak filesystem structure:
Expected dict, got str/list= the file exists and was read into the process;FileNotFoundError: '<abs path>'= missing;PermissionError: '<abs path>'= exists but unreadable. An attacker uses this to probe arbitrary paths and recover the operator's absolute filesystem layout. -
Verbatim secret disclosure into the generated code. When the referenced node is JSON-Schema-shaped, values in
const/default/enum/descriptionpositions are emitted verbatim into the generated Python that is returned to the attacker — i.e. exactly the internal/private schema and config files a codegen host stores (specs with example/default tokens, default credentials, internal hostnames, service-account JSON, k8s secret manifests).
Scope note (to avoid overstating): the raw bytes of unstructured files such as /etc/passwd or a PEM id_rsa are read into the process but are not echoed verbatim into the output, because they parse to a single scalar and are rejected as "not a schema". For those files the impact is the read itself plus the existence/permission/absolute-path oracle; verbatim content disclosure applies to schema-shaped nodes.
PoC
Self-contained reproducer: https://gist.github.com/thegr1ffyn/2a87e81f985883acc30d0118c52da4d3 / (poc.py creates everything in a temp dir, runs the generator, asserts read + leak + gate bypass, and cleans up).
Minimal manual reproduction of the arbitrary read (any path works):
Impact
Arbitrary local file read / path traversal (CWE-22) → information disclosure (CWE-200), plus bypass of --no-allow-remote-refs. Any application, CI pipeline, or multi-tenant service that runs datamodel-code-generator on an untrusted schema and exposes (returns, logs, commits, renders) the generated code is affected. The attacker can: open and read any file the process user can read (demonstrated: /etc/passwd, a PEM id_rsa, paths under /root/); map the host filesystem and recover absolute paths; and exfiltrate secret values verbatim from any schema-shaped node. Attacker controls only the input schema; no authentication or special privileges required.
Suggested remediation
- Do not exempt
file://from theallow_remote_refsgate, treat it as an external scheme. - In both
_get_ref_body_from_url(thefile://case) and_get_ref_body_from_remote, reject targets that are notresolved.is_relative_to(self.base_path), mirroring the existing check in_get_ref_body_from_local_http_path.
Maintainer status
Confirmed by maintainer review and regression tests. The private fix PR was merged and released in 0.62.0: https://github.com/koxudaxi/datamodel-code-generator-ghsa-8359-h9fx-j6v9/pull/1
Fix summary: apply the remote-ref gate to file:// refs and local JSON Schema refs outside the input base path. In 0.62.0, --no-allow-remote-refs blocks these references; the default compatibility mode emits a FutureWarning and users who intentionally rely on trusted external local refs can pass --allow-remote-refs.
Release status: fixed in 0.62.0 for the documented --no-allow-remote-refs bypass, with a compatibility warning for the default behavior.
Validation: uv run --group test --extra http pytest tests/main/jsonschema/test_main_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)
Пакеты
datamodel-code-generator
<= 0.61.0
0.62.0
Связанные уязвимости
datamodel-code-generator generates Pydantic v2 models, dataclasses, TypedDict, and msgspec.Struct from OpenAPI, JSON Schema, GraphQL, Avro, Protobuf, and raw JSON, YAML, or CSV. Prior to 0.62.0, datamodel-code-generator resolves JSON Schema $ref targets in src/datamodel_code_generator/parser/jsonschema.py through is_url and _get_ref_body without containing file:// or ../ traversal references to the input directory and without honoring --no-allow-remote-refs, allowing arbitrary local file reads. This issue is fixed in version 0.62.0.