# Sandbox escape via host intrinsics

Sandboxed code reaching host prototypes, Function constructors, or frames through fallback paths and incomplete blocklists

_Area: Web injection and code execution. Seen in 3 disclosed reports; latest 2026-05-14. Updated 2026-10-25._

## How it works
A sandbox assumes user code cannot reach the host realm's real objects. That assumption breaks when the boundary leaks a host object or forwards a mutation to host state. Once user code holds any host-realm object, its `constructor.constructor` chain yields the host `Function` constructor, and code compiled there runs outside the sandbox. A bridge that writes sandbox mutations into shared host prototypes lets user code poison intrinsics the host later uses. In Python, a blocklist that misses frame-traversal attributes lets a caught exception walk `__traceback__`/`tb_frame`/`f_builtins` back to real builtins.

## Where it shows up
- Sandbox selection gated on an env var, with a weaker fallback (e.g. NodeVM) when unset [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-9rvc-vf7m-pgm2.md)
- Host-realm Error objects crossing the VM boundary, exposing `constructor.constructor` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-9rvc-vf7m-pgm2.md)
- Bridge code that special-cases `__proto__` or returns host prototypes from `get` [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-vwrp-x96c-mhwq.md)
- `set`/`defineProperty` traps forwarding writes to real host intrinsics instead of sandbox-local copies [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-vwrp-x96c-mhwq.md)
- Two parallel blocklists for the same protection; the weaker one is the surface [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-qf73-2hrx-xprp.md)
- Attribute guards that hook `getattr()` but not dot-notation access [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-qf73-2hrx-xprp.md)
- Blocklists omitting `__traceback__`, `tb_frame`, `f_back`, `f_builtins` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-qf73-2hrx-xprp.md)

## How to review
Identify every path that executes user code and confirm which sandbox actually runs. The invariant: no host-realm object, prototype, or function may be reachable from sandboxed code, and no sandbox mutation may reach shared host state. Check that fallback sandboxes are as strong as the primary, that bridge traps keep mutations sandbox-local, and that blocklists are complete and enforced on dot notation, not just `getattr()`. Treat the entry route's auth as a separate gate, not the escape itself.

## Variants seen
- Env-gated fallback to a weaker VM leaking host Error objects [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-9rvc-vf7m-pgm2.md)
- Prototype pollution of host intrinsics through bridge `set`/`defineProperty` [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-vwrp-x96c-mhwq.md)
- Exception frame traversal past an incomplete attribute blocklist [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-qf73-2hrx-xprp.md)

## Why fixes fail
E3 shows a fix applied to one blocklist but not the parallel subprocess copy, and a guard that only intercepts `getattr()`. Divergent copies of the same protection regress silently.

## Impact and severity
All three reached host-level code execution, the highest severity; E3 reached shell exec via builtins. Worse when the fallback is silent, since it is invisible in normal config review.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-9rvc-vf7m-pgm2.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-vwrp-x96c-mhwq.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-qf73-2hrx-xprp.md
