Circular JSON Schema $ref Causes Unbounded CPU DoS in json_repair Library
An unbounded CPU Denial-of-Service vulnerability exists in the `json_repair` library's `SchemaRepairer.resolve_schema()` function, allowing an unauthenticated attacker to provide a specially crafted JSON schema containing a circular `$ref` pointer, leading to indefinite CPU consumption and service unavailability.
An unauthenticated attacker can exploit a critical flaw in the json_repair library (versions prior to 0.60.1) to trigger a Denial of Service (DoS) condition. The vulnerability resides in the SchemaRepairer.resolve_schema() function, which is responsible for processing JSON schema $ref pointers. This function lacks cycle detection, meaning that if an attacker supplies a schema containing a self-referencing $ref (e.g., {"$ref": "#/definitions/a", "definitions": {"a": {"$ref": "#/definitions/a"}}}), the library enters an infinite loop. This unbounded CPU consumption by the worker process effectively halts the service, making it unavailable to other users. The vulnerability is reproducible and has been assigned a high severity CVSS score of 7.5. Applications that pass untrusted input to json_repair.loads(..., schema=...), including the library's public demo Flask API, are susceptible.
Attack Chain
- An unauthenticated attacker sends an HTTP POST request to a web application endpoint (e.g.,
/api/repair-jsonin the demo Flask API) that accepts user-controlled JSON input. - The request body contains a specially crafted JSON payload that includes a
schemafield with a circular$refdefinition, such as{"$ref": "#/definitions/a", "definitions": {"a": {"$ref": "#/definitions/a"}}}. - The vulnerable application extracts the user-provided
schemafrom the HTTP request body and passes it directly to thejson_repair.loads()function for processing. - Internally,
json_repair.loads()instantiates aSchemaRepairerobject and calls itsis_valid()method, which subsequently invokes theSchemaRepairer.resolve_schema()function. - Within
SchemaRepairer.resolve_schema(), awhileloop attempts to resolve the$refchain without any cycle detection. Due to the circular$ref, the internal_resolve_ref()function continuously returns the same dictionary object. - The
whileloop never terminates, leading to an unbounded consumption of CPU resources by the application's worker process. - This indefinite CPU spin causes the affected worker process to hang indefinitely, making the application unresponsive and inaccessible.
- The consequence is a complete Denial of Service for other legitimate users attempting to access the service.
Impact
This vulnerability results in an unauthenticated denial-of-service condition affecting any application that utilizes the json_repair library and exposes json_repair.loads(..., schema=<user-controlled>) to untrusted input. A single malicious HTTP request carrying a circular $ref schema can cause a Flask worker process (or similar) to consume CPU indefinitely. This makes the service unresponsive and unavailable, requiring manual intervention such as killing the hung process or restarting the server. The attack requires no special privileges and deterministically reproduces the service disruption, though it does not affect data confidentiality or integrity.
Recommendation
- Patch the
json_repairlibrary to version0.60.1or higher immediately to address the identified vulnerability. - Implement strict input validation and sanitize all user-supplied JSON schemas before passing them to
json_repair.loads()with theschemaparameter, specifically checking for circular$refdefinitions to prevent the infinite loop.