# Unhandled rejection across sandbox bridge

Promises crossing a sandbox or plugin boundary without a rejection handler, crashing the host process

_Area: Web business logic and race conditions. Seen in 2 disclosed reports; latest 2026-10-05. Updated 2026-10-23._

## How it works
A sandbox or plugin bridge lets guest code call host functions. When a host function returns a Promise, the bridge hands it back to the guest. If the guest ignores that Promise and it rejects, Node's default unhandled-rejection behavior terminates the host process. The bridge's job is to mark host-returned Promises as handled (attach a swallow tail) before they cross. The assumption that breaks: that every path returning a host Promise passes through the sanitizer. It only holds for the paths someone remembered to patch.

## Where it shows up
- Bridge/proxy traps that return host objects: `apply`, `construct`, `get`, `set` — check each one separately.
- A sanitizer call (e.g. `markHostPromiseHandled`) present on one trap but absent on a sibling.
- Wrapping code that adds `.then`/`.catch` for the guest but never attaches a handler to the raw host Promise.
- Hardening applied only to sandbox-created Promises, not host-realm objects crossing in.
- Engine-scheduled callbacks (`FinalizationRegistry`, `WeakRef`, microtasks, timers) exposed bare, running outside any timeout wrapper [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-r4fx-v8hh-22mv.md).

## How to review
For every bridge trap that can return a host Promise, confirm the invariant: the raw host Promise has a rejection handler attached before it is returned to guest code, on every path, in both directions of the boundary. Enumerate the traps and check each one, not just the one a prior fix touched. For timeout guarantees, list every engine-scheduled callback and confirm each runs inside the accounting; a wall-clock timeout on the entry point does not cover GC-invoked callbacks [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-r4fx-v8hh-22mv.md).

## Variants seen
- `construct` trap missing the sanitizer that `apply` already had [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-2v2p-6j97-cjg9.md).
- Host-realm rejected Promise returned through `apply` without a host-side handler [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-gjq8-xm47-88rc.md).
- GC cleanup callback bypassing the `run()` timeout and blocking the event loop [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-r4fx-v8hh-22mv.md).

## Why fixes fail
Fixes were per-trap and per-direction: hardening `apply` left `construct` open [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-2v2p-6j97-cjg9.md); hardening sandbox-created Promises left host Promises open [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-gjq8-xm47-88rc.md). The bug class recurs at each unpatched sibling.

## Impact and severity
Host process termination from a single guest expression — a denial of service against the whole host, not just the sandbox [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-2v2p-6j97-cjg9.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-gjq8-xm47-88rc.md). Severity is high because the guest needs no special access, only the ability to ignore a returned Promise. Worse when the bridge is shared across tenants or the host serves other requests.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-2v2p-6j97-cjg9.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-gjq8-xm47-88rc.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-r4fx-v8hh-22mv.md
