PT-2026-65665 · Pypi · Datamodel-Code-Generator

Published

2026-07-28

·

Updated

2026-07-28

CVSS v3.1

8.2

High

VectorAV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N

Summary

datamodel-code-generator's built-in HTTP fetcher (http.get body) issues an httpx.GET against any URL passed to --url (or reached via a redirect chain) with no allow-list, no deny-list, no IP/host validation, and follow redirects=True. Loopback addresses, RFC1918 ranges, link-local (169.254.169.254 cloud metadata), unique-local IPv6 and any other network-accessible target are all reachable. The JSON/YAML response body is parsed as a schema and reflected into the generated .py source, exfiltrating the response to anyone with access to that file (commonly committed to a repository).

Details

Sink: src/datamodel code generator/http.py, get body (lines 31–61, at tag 0.60.1 / commit a321547e):
python
def get body(url, headers=None, ignore tls=False,
       query parameters=None, timeout=DEFAULT HTTP TIMEOUT) -> str:
  httpx = get httpx()
  try:
    response = httpx.get(
      url,
      headers=headers,
      verify=not ignore tls,
      follow redirects=True,     # (A)
      params=query parameters,
      timeout=timeout,
    )
  except Exception as e:
    ...
  if response.status code >= 400:
    ...
  content type = response.headers.get("content-type", "").lower()
  if "text/html" in content type:
    raise SchemaFetchError(...)     # (B) — only filter
  return response.text          # (C) → embedded in generated.py
  • (A) follows redirects unconditionally — a public URL → 302 → internal address chain works.
  • (B) the only filter is rejecting text/html. Non-HTML internal endpoints (JSON APIs, cloud metadata, admin services) pass through.
  • (C) the response body becomes the schema; its title, description, properties, etc. land in the generated .py as class attributes and Field(description=...) strings.
get body is called by parser/base.py:1326 ( get text from url), which is reached from CLI argument --url <URL>. (The $ref path is a separate advisory — see GHSA-D.)
Only affects users who installed the [http] extra (pip install 'datamodel-code-generator[http]').

PoC

A self-contained one-file PoC available here: https://gist.github.com/thegr1ffyn/18de777d6c800a3b47715425e3f3e8f5

Impact

Who is impacted. Anyone who runs datamodel-codegen with a --url they didn't fully verify, or who runs it inside a network with reachable internal services. Realistic scenarios:
  1. Trojan documentation / README. A blog post or README example reads datamodel-codegen --url https://schemas.example.com/user.json -o user.py. The attacker controls example.com, redirects to http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>, and the IAM credentials end up as a docstring in user.py.
  2. Internal port scan / disclosure. Iterating --url http://127.0.0.1:<port>/health probes localhost services; non-HTML, non-error responses confirm a service and leak its body into the generated file.
  3. CI poisoning. A PR adds a Makefile rule that calls datamodel-codegen --url $(SCHEMA URL); the CI runner reaches every internal service in its VPC and the response lands in PR artifacts.
Suggested fix. Resolve the URL host, reject loopback / private / link-local / multicast / reserved IPs by default, disable redirects by default (follow redirects=False), re-validate after each redirect if the user opts into following them, and add an --allow-private-network flag for opt-in legitimate use.

Maintainer resolution

This report was fixed together with GHSA-954p-556p-r752 by the private security PR koxudaxi/datamodel-code-generator-ghsa-rfr2-mq9m-x2qx#1, merged into the public repository as 5fdba4a09f2d7a9996a504975b7ef7d63e3715bb. Follow-up generated-file and coverage fixes were merged in koxudaxi/datamodel-code-generator#3279 and docs were synced in #3280. The patched release is 0.61.0.
The patch hardens the shared HTTP fetcher used by both direct CLI --url fetching and remote JSON Schema/OpenAPI $ref resolution:
  • validates HTTP(S) URLs before fetching;
  • blocks localhost, loopback, private, link-local, reserved, and other non-public network targets by default;
  • disables automatic redirect following and validates each redirect target before requesting it;
  • adds --allow-private-network / allow private network=True as an explicit opt-in for trusted internal schema endpoints.
Remote $ref fetching remains controlled by --allow-remote-refs; non-public/internal targets additionally require --allow-private-network.
Submitted by: Hamza Haroon (thegr1ffyn)

Fix

SSRF

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

GHSA-RFR2-MQ9M-X2QX

Affected Products

Datamodel-Code-Generator