json-repair Circular JSON Schema $ref Infinite Loop DoS
Passing a self-referencing JSON Schema $ref to json_repair's loads() function causes the schema resolver to loop forever, hanging the process and making any service that exposes this parameter to untr
The problem
SchemaRepairer.resolve_schema() in src/json_repair/schema_repair.py walks $ref chains in a plain while loop with no cycle detection. When _resolve_ref() returns the same dict object on every iteration (because the schema points back to itself), the condition "$ref" in schema_dict is always True and the loop never exits.
Any application that passes user-controlled input to loads(..., schema=<user_input>) is affected. The public demo Flask API at docs/app.py exposes this path without authentication, so a single HTTP request is enough to hang a worker process indefinitely.
Proof of concept
A working proof-of-concept for this issue in json-repair, with the exact payload below.
# Direct library call (process hangs; kill after 5 s)
python3 -c "
from json_repair import loads
schema = {'\$ref': '#/definitions/a', 'definitions': {'a': {'\$ref': '#/definitions/a'}}}
loads('{}', schema=schema)
"
# HTTP attack against the demo Flask API
curl -X POST http://127.0.0.1:5005/api/repair-json \
-H 'Content-Type: application/json' \
--data '{"malformedJSON":"{}","schema":{"\$ref":"#/definitions/a","definitions":{"a":{"\$ref":"#/definitions/a"}}}}'The root cause is CWE-835: a loop with an unreachable exit condition. resolve_schema() calls _resolve_ref(), which looks up the $ref path relative to self.root_schema. A self-referencing $ref (e.g., "#/definitions/a" pointing to another "#/definitions/a") causes _resolve_ref() to return the exact same Python dict object on every call, so the while "$ref" in schema_dict guard never becomes False.
The patch in 0.60.1 introduces a seen_schema_ids set (tracking id() of each visited dict) before entering the loop. On the first repeated id, it raises a SchemaDefinitionError instead of looping, breaking the cycle. The fix also adds a type guard requiring $ref to be a str, closing a second edge case.
The fix
Upgrade to json-repair 0.60.1 or later: pip install --upgrade json-repair. If upgrading is not immediately possible, validate any caller-supplied schema against a strict allowlist that rejects $ref keys, or wrap loads() calls in a subprocess with a hard timeout.
Related research
- high · 8.1CVE-2026-61668CVE-2026-61668: DIRAC PilotWrapper Disabled TLS Certificate Validation
- highClauster: Missing Authentication Bypass on Non-Loopback Dashboard
- high · 7.8CVE-2026-54071CVE-2026-54071: BabelDOC Arbitrary Code Execution via CMap Pickle Deserialization
- high · 7.7mcp-atlassian: Arbitrary Server-Side File Read via Attachment Upload Path Traversal