$ref causes unbounded CPU DoS in json_repairSchemaRepairer.resolve_schema() in json_repair follows JSON Schema $ref pointers in an unbounded while loop without any cycle detection. An attacker who can supply a schema containing a self-referencing $ref (e.g., via the demo Flask API or any application that passes untrusted input to loads(..., schema=...)), can cause a worker process to spin indefinitely on CPU, resulting in a complete denial of service. No authentication is required against the public demo API. The vulnerability is confirmed reproducible at CVSS 7.5 (High).
SchemaRepairer.resolve_schema() at src/json_repair/schema_repair.py:184–190 resolves $ref chains using a plain while loop:
# src/json_repair/schema_repair.py:184-190
schema_dict = cast("dict[str, Any]", schema)
while "$ref" in schema_dict:
ref = schema_dict["$ref"]
resolved = self._resolve_ref(ref)
if isinstance(resolved, bool):
return resolved
schema_dict = resolved
_resolve_ref() at src/json_repair/schema_repair.py:654–665 always resolves references relative to self.root_schema, which is initialised from the caller-supplied schema (src/json_repair/schema_repair.py:130). When the schema contains a circular reference such as:
{"$ref": "#/definitions/a", "definitions": {"a": {"$ref": "#/definitions/a"}}}
_resolve_ref() returns the same dict object on every iteration, so "$ref" in schema_dict is always True and the loop never terminates.
The vulnerable sink is reachable without authentication through the demo Flask API:
# docs/app.py:14, 21-36
data = request.get_json()
schema = data.get("schema")
if schema is not None and not isinstance(schema, (dict, bool)):
raise ValueError("schema must be a JSON object or boolean.")
...
if schema is not None:
loads_kwargs["schema"] = schema
parsed_json = loads(malformed_json, **loads_kwargs)
```...
0.60.1Exploitability
AV:NAC:LPR:NUI:NScope
S:UImpact
C:NI:NA:H7.5/CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H