Like worrying about a lock that can be re-keyed — but only if someone already left the key blank
CVE-2026-88038 affects the npm cookies package (pillarjs/cookies) in all versions before 0.9.2. The library constructs Set-Cookie response headers by concatenating domain and path option values directly into the header string. While cookie *names* and *values* are validated against character sets that reject semicolons, the domain and path parameters only pass through permissive RFC 7230 validation that allows semicolons. An attacker who can influence the value passed to domain or path — for example via domain: '.' + req.headers.host — can inject additional cookie attributes (SameSite, Secure, HttpOnly, Domain) into the response header. The package has ~1.9 million weekly npm downloads and is a core dependency of the Koa framework and various Express middleware stacks.
The vendor's MEDIUM 4.8 score is honest but arguably still generous. The CVSS vector correctly sets AC:H because exploitation requires the application to pass *untrusted, request-derived input* into the domain or path cookie options — a pattern most production codebases simply don't use. Developers overwhelmingly hardcode these values or pull them from server-side configuration. Even when the chain succeeds, the impact ceiling is limited to weakening cookie security flags (stripping HttpOnly, overriding SameSite), which is a *precondition* for further attacks like CSRF or session fixation — not a direct compromise. There is no RCE, no authentication bypass, and no availability impact. noisgate downgrades to LOW 3.5.
3 steps from start to impact.
Identify application passing untrusted input to cookie options
domain or path options are derived from request-controlled input. The canonical anti-pattern is domain: '.' + req.headers.host or similar constructs that reflect a user-supplied hostname into the cookie's domain attribute. Without this application-level coding pattern, the vulnerability is unreachable.- Application uses pillarjs/cookies < 0.9.2
- Application passes attacker-influenced input to
domainorpathcookie options
- Most applications hardcode domain/path in config or environment variables
- Code review and linting rules commonly flag request-derived cookie parameters
- This anti-pattern is not present in Koa's default session middleware
req.headers flowing into cookie option objects.Inject semicolon-delimited attributes via Host header
Host header (or other reflected input) containing a semicolon followed by cookie attribute key-value pairs, e.g., evil.com; SameSite=None; Secure. The cookies library concatenates this into the Set-Cookie header without sanitization, causing the browser to parse the injected attributes as part of the cookie directive.- Attacker can send arbitrary Host headers to the application
- No upstream reverse proxy normalizes or rejects malformed Host headers
- Most production deployments sit behind nginx, Cloudflare, or an ALB that normalizes Host headers
- WAF rules commonly block semicolons in Host headers
- Koa's
app.proxysetting andX-Forwarded-Hosthandling add additional layers
;, \r, \n). Server-side logging of Set-Cookie response headers can catch injected attributes.Weaken victim's session cookie protections
HttpOnly (exposing the cookie to XSS), override SameSite=Lax to SameSite=None (enabling cross-site request attachment), or broaden Domain scope (sharing the cookie with sibling subdomains). These changes weaken the security posture of the victim's session cookie but do not directly steal it. A separate attack (XSS, CSRF) must still be chained to exploit the weakened cookie.- Successful attribute injection from step 2
- A second vulnerability (XSS or CSRF vector) to exploit the weakened cookie protections
- Modern browsers default SameSite=Lax when not specified, limiting the CSRF window
- CSP headers and other defense-in-depth measures reduce XSS exploitability independently
- The attacker must chain a second, separate vulnerability to gain any meaningful impact
The supporting signals.
| In-the-wild exploitation | None observed. No reports from any threat intelligence feed, no CISA KEV listing, no vendor acknowledgment of active exploitation. |
|---|---|
| Proof-of-concept | None public. The advisory describes the injection mechanism conceptually but no weaponized PoC repository or researcher demo has been published as of 2026-09-10. |
| EPSS score | Not yet scored. Advisory was published 2026-09-10 (today); EPSS lag means no score is available yet. Expected to land in the low single-digit percentile given AC:H and no known exploitation. |
| KEV status | Not listed. Not on the CISA Known Exploited Vulnerabilities catalog. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N — Network-reachable but high attack complexity, no privileges or user interaction needed, unchanged scope, low confidentiality and integrity impact, no availability impact. |
| Affected versions | pillarjs/cookies < 0.9.2 (all prior releases) |
| Fixed version | 0.9.2 — adds semicolon, CR, and LF rejection to domain and path option validation. |
| Scanning / exposure | ~1.9M weekly npm downloads; top 1% of npm packages. Core dependency of Koa (~8M downloads/week) and egg.js. However, vulnerable *application pattern* (untrusted input → cookie options) is a small subset of that install base. |
| Disclosure date | 2026-09-10 (GHSA-x44v-5gxf-r6hf published today) |
| Reporter / credits | Remediation by UlisesGascon, reviewed by blakeembrey (pillarjs maintainer). |
noisgate verdict.
The single most decisive factor is that exploitation requires the application to pass attacker-controlled input into the domain or path cookie options — an uncommon coding anti-pattern that the vast majority of the 1.9M-download install base does not exhibit. Even successful exploitation only weakens cookie security flags rather than directly compromising authentication, data, or availability, making the *effective* blast radius negligible without a chained second vulnerability.
Why this verdict
- Attack complexity is genuinely high (AC:H is accurate): The vulnerability is only reachable when the consuming application passes untrusted, request-derived input into cookie
domainorpathoptions. This is a specific anti-pattern, not a default behavior of the library or its framework consumers (Koa, egg.js). - Impact ceiling is a stepping stone, not a destination: Successful injection weakens cookie attributes (
SameSite,HttpOnly,Secure) but does not directly yield credential theft, RCE, or data exfiltration. A second, independent vulnerability must be chained for meaningful impact. The CVSS C:L/I:L/A:N accurately reflects this. - Reverse-proxy friction eliminates most real-world paths: Production Node.js deployments almost universally sit behind nginx, Cloudflare, AWS ALB, or similar reverse proxies that normalize or reject malformed
Hostheaders containing semicolons, cutting off the primary injection vector before it reaches application code. - Role multiplier: pillarjs/cookies is a web-application-tier library. In high-value roles (IdP built on Koa, CI/CD dashboard), the chain could weaken session cookie protections — but the outcome is still 'weakened cookie flags requiring a chained attack,' not 'domain takeover / fleet compromise / mass data egress.' The blast radius is tenant-scoped at most and conditional on a second vuln. Floor does not trigger.
- No exploitation evidence or tooling: Zero in-the-wild activity, no public PoC, no KEV listing, advisory published today. The absence of any attacker interest further supports a lower severity.
Why not higher?
A MEDIUM rating would require either a realistic path to direct compromise without chaining or evidence that the vulnerable coding pattern is common in production. Neither condition is met. The AC:H constraint is not theoretical — it requires a specific, uncommon application anti-pattern. Even the 1.9M weekly downloads represent *library* adoption, not *vulnerable-pattern* adoption; the actual exploitable population is a small fraction of that base.
Why not lower?
An IGNORE rating would be inappropriate because the library is extremely widely deployed and a non-trivial number of applications *could* exhibit the vulnerable pattern, especially multi-tenant SaaS platforms that dynamically scope cookies to tenant-supplied domains. The fix is a single npm update and the risk, while low, is non-zero. Tracking and patching within a backlog cycle is warranted.
What to do — in priority order.
- Audit application code for untrusted input flowing into cookie options — Search your codebase for calls to
cookies.set()or Koa'sctx.cookies.set()where thedomainorpathoption is derived fromreq.headers, query parameters, or any user-controlled source. If no such pattern exists, your application is not exploitable regardless of library version. This is a one-time audit — no ongoing SLA since verdict is LOW. - Ensure reverse proxy normalizes Host headers — Verify that your upstream reverse proxy (nginx, Cloudflare, ALB, Envoy) rejects or strips semicolons, carriage returns, and newlines from the
Hostheader before forwarding to Node.js. This blocks the primary injection vector at the network edge. - Upgrade to cookies 0.9.2 — Run
npm update cookiesor pin>=0.9.2in package.json. This is the definitive fix — the new version rejects semicolons, CR, and LF indomainandpathoptions. Per the LOW verdict, treat this as backlog hygiene with no hard SLA.
- CSP headers — Content Security Policy governs script execution and resource loading, not Set-Cookie header construction. CSP does not prevent cookie attribute injection.
- HttpOnly flag on other cookies — Setting HttpOnly on your session cookie is good practice but does not prevent *this vulnerability from overriding it*. The injection can strip HttpOnly from the affected cookie.
- Cookie signing (Keygrip) — pillarjs/cookies supports signed cookies, but signing validates cookie *values*, not *attributes*. The injected
SameSite=NoneorDomain=.evil.comattributes are not covered by the signature.
Crowdsourced verification payload.
Run this on any machine with Node.js and npm/npx available. It checks the installed version of the cookies package in the current project directory. No special privileges required. Example: bash check_cve_2026_88038.sh /path/to/your/node/project
#!/usr/bin/env bash
# check_cve_2026_88038.sh — Detect CVE-2026-88038 in pillarjs/cookies
# Usage: bash check_cve_2026_88038.sh [project_dir]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
PKG_JSON="$PROJECT_DIR/node_modules/cookies/package.json"
if [ ! -f "$PKG_JSON" ]; then
echo "UNKNOWN — cookies package not found in $PROJECT_DIR/node_modules/"
exit 2
fi
VERSION=$(node -e "console.log(require('$PKG_JSON').version)" 2>/dev/null)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse version from $PKG_JSON"
exit 2
fi
# Compare version against fixed version 0.9.2
RESULT=$(node -e "
const v = '$VERSION'.split('.').map(Number);
const fix = [0, 9, 2];
for (let i = 0; i < 3; i++) {
if ((v[i]||0) < fix[i]) { console.log('VULNERABLE'); process.exit(0); }
if ((v[i]||0) > fix[i]) { console.log('PATCHED'); process.exit(0); }
}
console.log('PATCHED');
")
echo "$RESULT — cookies version $VERSION (fixed in 0.9.2)"
if [ "$RESULT" = "VULNERABLE" ]; then
exit 1
elif [ "$RESULT" = "PATCHED" ]; then
exit 0
else
exit 2
fiIf you remember one thing.
domain or path options — an uncommon pattern. noisgate reassesses this as LOW (3.5), downgraded from the vendor's MEDIUM 4.8. There is no noisgate mitigation SLA for LOW — treat this as backlog hygiene. First, run a quick codebase grep for cookies.set calls where domain or path derives from request headers; if you find none, you are not exploitable and can deprioritize freely. If you *do* find the pattern, bump cookies to 0.9.2 (npm update cookies) and harden your reverse proxy to strip semicolons from Host headers. No hard remediation deadline applies, but folding the upgrade into your next dependency refresh cycle is good housekeeping.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.