json_repair Circular Schema Reference Can Pin AI Service Workers — GHSA-xf7x-x43h-rpqh

The Python json-repair package has patched a high-severity denial-of-service flaw in its optional JSON Schema processing. In versions through 0.59.10, a schema containing a circular $ref can send SchemaRepairer.resolve_schema() into an unbounded loop, consuming a worker's CPU until the process is terminated. Version 0.60.1 contains the fix.

The project is designed to repair malformed JSON from LLMs, APIs, logs, and user input, making the advisory relevant to AI pipelines that use repair as a tolerance layer around model output. The important boundary is narrower than “malformed model JSON causes DoS”: exploitation requires an attacker to influence the schema passed to loads(..., schema=...). Applications that keep schemas static and trusted are not exposed through ordinary untrusted JSON alone.

How the loop becomes unreachable

The vulnerable resolver follows $ref entries in a while loop. Before 0.60.1, it did not keep a visited set, impose a reference-depth limit, or otherwise reject cycles. A self-reference can therefore resolve to the same dictionary on every iteration while the loop condition remains true forever.

The vendor advisory demonstrates the condition with a schema whose definition points back to itself. In the project's Flask demo path, a caller-supplied schema object passes a top-level type check and reaches json_repair.loads() without cycle detection. The reproduced baseline completed normally, while the circular case was still running when the test killed it after five seconds.

GitHub rates the advisory High, CVSS 3.1 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) and maps it to CWE-835, Loop with Unreachable Exit Condition. The demonstrated effect is availability loss, not data disclosure or modification. One request can occupy one synchronous worker indefinitely; the service-level impact depends on worker count, process supervision, CPU quotas, and whether an attacker can repeat the request.

Why AI infrastructure should care

JSON repair libraries often sit directly on an availability-sensitive boundary: between probabilistic model output and deterministic application code. That location encourages permissive parsing and retry behavior, but it should not imply permissive control over the schema. If a multi-tenant evaluation service, agent tool, structured-output gateway, or public “repair” API lets callers submit both payload and schema, the schema is executable validation policy and must be treated as hostile input.

The flaw also shows why timeouts around upstream model calls are insufficient. The runaway work happens locally during schema resolution, after input has reached the application process. An HTTP deadline may stop the client waiting, but it does not necessarily stop a CPU-bound Python worker unless the deployment enforces process-level cancellation or replacement.

Defensive engineering guidance

  • Upgrade to json-repair 0.60.1 or later. Inventory direct and transitive installations and rebuild long-running images or workers so the patched code is actually loaded.
  • Do not accept arbitrary schemas by default. Select immutable, server-owned schemas by identifier. If tenant-defined schemas are required, validate their reference graph before repair and reject circular, excessively deep, or external references.
  • Separate payload limits from schema limits. Bound JSON body size, schema size, nesting depth, definition count, and reference traversal independently.
  • Contain parser work. Run untrusted repair and validation in workers with CPU and wall-clock limits, finite queues, health checks, and supervisor-enforced replacement. A request timeout alone is not a CPU limit.
  • Monitor saturation signals. Alert on workers consuming sustained CPU without completing requests, repair latency outliers, repeated schema failures, queue growth, and restarts tied to structured-output endpoints.
  • Test the trust boundary. Add cyclic and deeply chained references to security tests for every endpoint that accepts JSON Schema. Confirm that failure is fast, bounded, and does not trigger retry storms.

The patch closes the known infinite loop, but the broader engineering lesson is to keep model-output tolerance narrow. Repairing malformed data can improve reliability; allowing an untrusted caller to define the repairer's recursive schema turns that reliability layer into a denial-of-service surface.

Sources: