Every CVE Search/ Get API key

CVE-2026-55389

datamodel-code-generator vulnerable to arbitrary local file read via JSON-Schema `$ref` (`file://` and `../` traversal), bypassing `--no-allow-remote-refs`

7.5 HighFix available

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

0.55%Up 0.2 points since Jul 29, 2026
90%50%10%1%

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 HighCVSS: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 HighCVSS: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:

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

  2. Verbatim secret disclosure into the generated code. When the referenced node is JSON-Schema-shaped, values in const/default/enum/description positions 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 HighCVSS: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 HighCVSS: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 HighCVSS: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:

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

  2. Verbatim secret disclosure into the generated code. When the referenced node is JSON-Schema-shaped, values in const/default/enum/description positions 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 HighCVSS: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:

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

  2. Verbatim secret disclosure into the generated code. When the referenced node is JSON-Schema-shaped, values in const/default/enum/description positions 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 HighCVSS: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

Affected packages
PackageAffectedFixed
datamodel-code-generator
PyPI
through 0.61.0
0.62.0
datamodel-code-generator
PyPI
before 0.62.0, plus listed versions
0.0.10.0.20.0.30.0.40.0.5
Show 45 moreShow less
0.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

History
RecordedChangedSources
first indexedCVE 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

This page doesn't list everything

Left out here: 202 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open