# Sandbox escape to Function constructor

Sandboxed JS/expression engines reaching Function or eval via overridable internals or same-origin bridges

_Area: Web injection and code execution. Seen in 3 disclosed reports; latest 2026-08-21. Updated 2026-10-24._

## How it works
A sandbox assumes the code it runs cannot reach the host's dynamic-evaluation primitives (Function, eval). That assumption breaks when the sandbox's own internals are attacker-reachable: a patched library method calls a method on an attacker-supplied object, an internal call path reads a property the expression can set, or the runtime shares the host's origin and credentials. In each case the boundary is crossed not by defeating the sandbox directly but by steering its trusted code into calling something the attacker controls.

## Where to show up
- User input string-interpolated into generated sandboxed code, especially after only a URL/format check [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md)
- Validators that accept fragments or trailing content the code generator then embeds [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md)
- Sandboxed engines exposing transform/update operators that mutate internal objects [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-66mm-25pp-rfff.md)
- Internal call paths using `obj.method.forEach` or similar attacker-reachable properties instead of prototype methods [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-66mm-25pp-rfff.md)
- Client-side interpreters (Pyodide, WASM) in same-origin workers whose HTTP bridge carries session cookies [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md)
- Nodes or configs that disable the sandbox (`useSandbox:false`) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md)

## How to review
Trace every path from user input to code generation or evaluation. The invariant: no attacker-controlled value may reach a property lookup, method call, or string concatenation inside trusted sandbox code without being coerced to a primitive first. Check that validators reject or strip fragments and that interpolation escapes. For expression engines, confirm clone semantics cannot be overridden and internal iteration uses `Array.prototype.forEach.call` on a copied array. For same-origin interpreters, confirm the bridge cannot make credentialed requests to privileged APIs.

## Variants seen
- URL hash smuggled through `new URL()` into sandboxed JS, bypassing a patched locale check via overridden `match()` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md)
- Expression overwrites `$clone`, mutates lambdas, abuses `proc.arguments.forEach` to reach Function [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-66mm-25pp-rfff.md)
- Same-origin Pyodide worker turns stored payload into credentialed API calls, creating server-side Function [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md)

## Impact and severity
All three reached remote code execution or equivalent server-side capability. Severity is high: sandbox escape yields host compromise. Worse when the entry point is authenticated-but-low-privilege [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md) or stored and shared across users [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md), widening reach.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-66mm-25pp-rfff.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-4r2p-27mh-5m22.md
