CVE-2026-55389
datamodel-code-generator vulnerable to arbitrary local file read via JSON-Schema `$ref` (`file://` and `../` traversal), bypassing `--no-allow-remote-refs`
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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.5
datamodel-code-generator vulnerable to arbitrary local file read via JSON-Schema `$ref` (`file://` and `../` traversal), bypassing `--no-allow-remote-refs`
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.
- Record
CVE-2026-55389- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-200, CWE-610
- Updated
- 2026-11-02
EUVDEUVD-2026-50078
7.5
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.
- Record
EUVD-2026-50078- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8359-h9fx-j6v9
7.5
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):
def is_url(ref: str) -> bool:
return ref.startswith(("https://", "http://", "file://"))
The remote-ref gate then explicitly exempts file://, so --no-allow-remote-refs never applies to it (parser/jsonschema.py, _get_ref_body):
if is_url(resolved_ref):
if not resolved_ref.startswith("file://") and self.http_local_ref_path is None:
if self.allow_remote_refs is False:
raise Error(...) # <-- skipped for file://
...
return self._get_ref_body_from_url(resolved_ref)
return self._get_ref_body_from_remote(resolved_ref)
Both local-file branches read the target with no containment check. The file:// branch reads any absolute path:
# _get_ref_body_from_url
if ref.startswith("file://"):
path = url2pathname(urlparse(ref).path) # absolute path, anywhere on disk
return self.remote_object_cache.get_or_put(
ref, default_factory=lambda _: load_data_from_path(Path(path), self.encoding))
and the plain relative branch lets ../ escape the base directory:
# _get_ref_body_from_remote
full_path = self.base_path / resolved_ref # no is_relative_to(base_path) check
return self.remote_object_cache.get_or_put(
str(full_path), default_factory=lambda _: load_data_from_path(full_path, self.encoding))
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 / …
- Record
GHSA-8359-h9fx-j6v9- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-200, CWE-610
- Updated
- 2026-11-02
NVD
7.5
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.
- Record
CVE-2026-55389- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-22, CWE-200, CWE-610
- Updated
- 2026-11-02
OSV
7.5
datamodel-code-generator vulnerable to arbitrary local file read via JSON-Schema `$ref` (`file://` and `../` traversal), bypassing `--no-allow-remote-refs`
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.
- Record
CVE-2026-55389- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-22, CWE-610
- Updated
- 2026-11-02
OSVGHSA-8359-h9fx-j6v9
7.5
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):
def is_url(ref: str) -> bool:
return ref.startswith(("https://", "http://", "file://"))
The remote-ref gate then explicitly exempts file://, so --no-allow-remote-refs never applies to it (parser/jsonschema.py, _get_ref_body):
if is_url(resolved_ref):
if not resolved_ref.startswith("file://") and self.http_local_ref_path is None:
if self.allow_remote_refs is False:
raise Error(...) # <-- skipped for file://
...
return self._get_ref_body_from_url(resolved_ref)
return self._get_ref_body_from_remote(resolved_ref)
Both local-file branches read the target with no containment check. The file:// branch reads any absolute path:
# _get_ref_body_from_url
if ref.startswith("file://"):
path = url2pathname(urlparse(ref).path) # absolute path, anywhere on disk
return self.remote_object_cache.get_or_put(
ref, default_factory=lambda _: load_data_from_path(Path(path), self.encoding))
and the plain relative branch lets ../ escape the base directory:
# _get_ref_body_from_remote
full_path = self.base_path / resolved_ref # no is_relative_to(base_path) check
return self.remote_object_cache.get_or_put(
str(full_path), default_factory=lambda _: load_data_from_path(full_path, self.encoding))
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 / …
- Record
GHSA-8359-h9fx-j6v9- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-22, CWE-610
- Updated
- 2026-11-02
OSVPYSEC-2026-3558
7.5
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):
def is_url(ref: str) -> bool:
return ref.startswith(("https://", "http://", "file://"))
The remote-ref gate then explicitly exempts file://, so --no-allow-remote-refs never applies to it (parser/jsonschema.py, _get_ref_body):
if is_url(resolved_ref):
if not resolved_ref.startswith("file://") and self.http_local_ref_path is None:
if self.allow_remote_refs is False:
raise Error(...) # <-- skipped for file://
...
return self._get_ref_body_from_url(resolved_ref)
return self._get_ref_body_from_remote(resolved_ref)
Both local-file branches read the target with no containment check. The file:// branch reads any absolute path:
# _get_ref_body_from_url
if ref.startswith("file://"):
path = url2pathname(urlparse(ref).path) # absolute path, anywhere on disk
return self.remote_object_cache.get_or_put(
ref, default_factory=lambda _: load_data_from_path(Path(path), self.encoding))
and the plain relative branch lets ../ escape the base directory:
# _get_ref_body_from_remote
full_path = self.base_path / resolved_ref # no is_relative_to(base_path) check
return self.remote_object_cache.get_or_put(
str(full_path), default_factory=lambda _: load_data_from_path(full_path, self.encoding))
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 / …
- Record
PYSEC-2026-3558- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
datamodel-code-generatorPyPI |
through 0.61.0 | 0.62.0 |
datamodel-code-generatorPyPI |
before 0.62.0, plus listed versions 0.0.10.0.20.0.30.0.40.0.5 Show 45 moreShow less0.0.60.1.00.10.00.10.10.10.20.10.30.11.00.11.10.11.100.11.110.11.120.11.130.11.140.11.150.11.160.11.170.11.180.11.190.11.20.11.200.11.30.11.40.11.50.11.60.11.70.11.80.11.90.12.00.12.10.12.20.12.30.13.00.13.10.13.20.13.30.13.40.13.50.14.00.14.10.15.00.16.00.16.10.17.00.17.10.17.2 |
0.62.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-55389 is a high vulnerability (CVSS 7.5) in Koxudaxi Datamodel-code-generator, published July 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.55%, the 46th percentile. Fixed in datamodel-code-generator 0.62.0.
References
github.com/koxudaxi/datamodel-code-generator/security/advisories/GHSA-8359-h9fx-j6v9
github.com/koxudaxi/datamodel-code-generator/commit/2ff4a72b4550a2b2069754c5b075b1655067e5fb
github.com/koxudaxi/datamodel-code-generator/releases/tag/0.62.0
github.com/advisories/GHSA-8359-h9fx-j6v9
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55389.json
nvd.nist.gov/vuln/detail/CVE-2026-55389
github.com/koxudaxi/datamodel-code-generator
pypi.org/project/datamodel-code-generator
Left out here: 202 versions and the rest of 3 source descriptions. The JSON record has them.