# V8 CDP Leak Probe — Validation Results (2026-06-22)

## Setup
- **Tool:** `test.py` (dual-runner: vanilla Playwright vs Chrome --remote-debugging-port)
- **Server:** `python3 -m http.server 3457 --bind 127.0.0.1` (LOCAL, NOT Vercel)
- **Test page:** `demo.html` (inline probes, no network during measurement)
- **Calibration:** 2-test proof — clean baseline + CDP-attached, same machine

## Results

| Signal | Vanilla (no CDP) | CDP-attached | Verdict |
|---|---|---|---|
| `v8ProtoProxyFired` | **True** ❌ | True | FALSE POSITIVE — fires on every Chrome |
| `v8SubclassFired` | **True** ❌ | True | FALSE POSITIVE — universal V8 console behavior |
| `consoleDebugTimingDelta` | -0.2ms | 0.1ms | Inverted from research prediction — useless |
| `chromeKeyCount` | **0** ✅ | **3** ✅ | **VALIDATED — clean separation** |

## Why the V8 probes failed
The 2026-06-22 nightly research claimed the V8 prototype-Proxy and Error subclass bypasses were
"spec-level requirements — V8 can't opt out" and only fired under CDP. **Wrong.** V8's console
formatter calls `ownKeys` on the prototype chain and walks `Error.message` getter on EVERY
browser context regardless of CDP attachment. The probes fire on vanilla Playwright because
the console pipeline is the same. CDP ≠ DevTools, and the research conflated them.

## The one signal that worked: `window.chrome` key count

Real Chrome (with or without `--remote-debugging-port` attached) has `window.chrome` with
3 keys: `app, csi, loadTimes`. Vanilla Playwright Chromium reports `0` (no `window.chrome`).
**Stealth browsers typically fake `window.chrome` to look real — but get the structure wrong.**
A real Chrome exposing CDP via the remote port still has the real `window.chrome`; stealth
browsers that add it manually often miss the exact 3-key structure.

**Hypothesis:** `chromeKeyCount !== 3` in a CDP-attached context (or `chromeKeyCount` exactly
3 in a context claiming to be CDP but not actually attached) is a useful signal.

## Next steps (not yet validated)
1. **Test against actual stealth browsers** (CloakBrowser, Camoufox, Rebrowser, ShardX):
   - Do they fake `window.chrome` with exactly 3 keys?
   - Do they fake the 3 keys in the right order?
2. **Test on real human Chrome with DevTools open** (not just CDP):
   - Does `chromeKeyCount` stay 3? (should — DevTools doesn't replace window.chrome)
3. **Test on iOS Safari** (excluded from current measurement — different runtime):
   - iOS doesn't have `window.chrome` at all → need a different probe family

## Files
- `demo.html` — the measurement page (V8 probes + chromeKeyCount, all inline)
- `test.py` — dual-runner validation harness (vanilla + CDP)
- `VALIDATION-RESULTS.md` — this file

## Anti-pattern (DO NOT REPEAT)
"Spec-level requirement" + "V8 can't opt out" + "Reddit thread confirms" ≠ validated signal.
The V8 console pipeline runs the same code path on every browser. Only local 2-test proof
counts. This is why the V8 probe code in `src/app/api/script/route.ts` was reverted
in commit 29ace13.
