Severity: Critical (decompression bomb) + Medium (entity expansion). Found in a security sweep of v0.8.0. Both are reachable by anyone who can open an SMTP connection to the inbound receiver (port 25/2525) and send one crafted message — no auth.
1. Decompression bomb (no output size cap)
src/modules/dmarc-reports/services/dmarc-parser.service.ts:61 / :69:
if (format === "gzip") return strFromU8(gunzipSync(bytes)); // no cap
const entries = unzipSync(bytes); // no cap
The inbound path caps the raw message at 10 MB, but a gzip/zip attachment inside it can expand ~1000:1 (fflate enforces no ratio limit). gunzipSync/unzipSync are synchronous — they allocate the whole output and block the event loop. A ~7 MB attachment → multiple GB → OOM / full freeze. The DMARC branch runs on every inbound message (smtp-receiver.service.ts:386), gated only by a spoofable looksLikeDmarcReport() (subject/from + zip/gzip content-type).
Exploit: email to any registered domain, Subject: DMARC aggregate report, 42 KB gzip → 5 GB → inbound receiver (and the whole process) goes down.
Fix: streaming/limited inflate with a hard maxOutputSize (e.g. 20–50 MB), abort past it; run off the main loop.
2. XML entity expansion (billion laughs)
dmarc-parser.service.ts:216:
const xmlParser = new XMLParser({ ignoreAttributes: true, parseTagValue: true });
processEntities defaults to enabled and there's no DOCTYPE rejection. fast-xml-parser doesn't fetch external entities (no classic XXE file-read/SSRF here), but internal nested <!ENTITY> definitions expand during the synchronous parse() and peg CPU / stall the loop.
Fix: pass processEntities: false and/or reject input containing <!DOCTYPE.
Impact
A mail platform whose inbound receiver can be crashed or frozen by a single crafted email is unusable as an MX — repeated outages from trivial mail is a reputation-ender.
Acceptance
Severity: Critical (decompression bomb) + Medium (entity expansion). Found in a security sweep of v0.8.0. Both are reachable by anyone who can open an SMTP connection to the inbound receiver (port 25/2525) and send one crafted message — no auth.
1. Decompression bomb (no output size cap)
src/modules/dmarc-reports/services/dmarc-parser.service.ts:61/:69:The inbound path caps the raw message at 10 MB, but a gzip/zip attachment inside it can expand ~1000:1 (fflate enforces no ratio limit).
gunzipSync/unzipSyncare synchronous — they allocate the whole output and block the event loop. A ~7 MB attachment → multiple GB → OOM / full freeze. The DMARC branch runs on every inbound message (smtp-receiver.service.ts:386), gated only by a spoofablelooksLikeDmarcReport()(subject/from + zip/gzip content-type).Exploit: email to any registered domain,
Subject: DMARC aggregate report, 42 KB gzip → 5 GB → inbound receiver (and the whole process) goes down.Fix: streaming/limited inflate with a hard
maxOutputSize(e.g. 20–50 MB), abort past it; run off the main loop.2. XML entity expansion (billion laughs)
dmarc-parser.service.ts:216:processEntitiesdefaults to enabled and there's no DOCTYPE rejection. fast-xml-parser doesn't fetch external entities (no classic XXE file-read/SSRF here), but internal nested<!ENTITY>definitions expand during the synchronousparse()and peg CPU / stall the loop.Fix: pass
processEntities: falseand/or reject input containing<!DOCTYPE.Impact
A mail platform whose inbound receiver can be crashed or frozen by a single crafted email is unusable as an MX — repeated outages from trivial mail is a reputation-ender.
Acceptance
XMLParsercreated withprocessEntities: false(+ DOCTYPE reject).THREAT_MODEL.mdinbound section updated.