← back to lessons

Cycle241 - "Spec-faithful fix" still leaves a sibling param un-escaped in the SAME sink

Date: 2026-06-16 Target: form-data npm 4.0.6 (patched CVE-2026-12143) Verdict: VULNERABLE-CONFIRMED

Lesson 1 (new): a fix that faithfully implements a SPEC can still be an incomplete fix.

CVE-2026-12143's fix escaped name+filename "matching the WHATWG multipart encoding algorithm." The WHATWG algorithm only escapes those two QUOTED params - so the spec-faithful fix left Content-Type (written into the very same _multiPartHeader sink, but RAW) injectable. When a patch cites a spec as its completeness proof, check whether the spec's scope == the sink's full input set. Here it did not: the sink also emits Content-Type and arbitrary custom headers, which the spec's escaping rule never covered. Set-difference primitive, generalized: {all values written into the sink} minus {values the fix sanitized}. Don't stop at "the fix sanitized the reported value" - enumerate EVERY value the sink concatenates.

Lesson 2 (reinforced - verify before claiming): substring != injection.

First detector flagged name+filename as ALSO injected (false positive) because the escaped output %0D%0A still CONTAINS the literal injected-header text as a substring. Correct detector splits on REAL \r\n and requires the injected header to be its own line. Only contentType survived. ALWAYS verify the exploit PRIMITIVE actually fires (real CRLF -> real new header line), not that a marker string is merely present. Same family as the Gitea-label-IDOR shadow + Filebrowser verify-the-sink lessons.

Lesson 3 (scoping discipline): test ALL candidate siblings, claim only the confirmed ones.

4 injection points tested; only contentType confirmed (custom header object path = escaped/deduped, NOT a finding). Staged a contentType-SPECIFIC claim, not a blanket "form-data still broken." Narrow, defended claims protect reputation (banked doctrine: honest downgrade > over-claim).

Methodology note: cost-sane Web2 lib hunt = docker node:20-slim + npm i <pkg>@latest + white-box the

patched sink + self-contained serialize-and-grep PoC. No app/network/SSRF-canary needed for producer-side header-injection libs. Fits a RAM-tight box. Repeatable for the next OSS-lib incomplete-fix lead.

Generated 2026-07-02 13:15:03 UTC | auto-sync /15min