Skip to content
All fingerprint checksAutomation surface

No console preview enumerated a prototype-chain Proxy

Check id console-preview-proxy-trap

What an ordinary browser yields

Nothing. On a browser with no Runtime client attached, a console argument is never previewed, the prototype is never enumerated, and the trap never fires.

What a detector infers

DevTools has to look inside an object to show it to you, and in JavaScript looking inside an object is an observable act. That tension is the whole signal. When the CDP Runtime domain is enabled — by an open DevTools window, or by Puppeteer, Playwright or anything else calling Runtime.enable — V8's inspector intercepts every console.* call and hands each argument to V8ConsoleMessage::wrapArguments with generatePreview set, so the panel can render an expandable view of it. Building that preview means enumerating the object's keys. Enumerating a plain object is a silent memory read; enumerating a Proxy is a function call, because the specification gives an engine no way to obtain a Proxy's own keys without invoking its ownKeys trap. So the page supplies the trap and learns, synchronously and deterministically, that something serialised its argument. The construction that reaches the trap is the interesting part, because Chromium's developers anticipated the direct attack and guarded it twice: buildObjectPreviewInternal unwraps a Proxy argument down to its target before previewing, and DebugPropertyIterator::Create skips past a Proxy receiver before collecting keys. Both guards test only the immediate value. The bait is therefore not a Proxy at all — it is a plain object created with a Proxy as its PROTOTYPE, so typeof reads "object", both guards pass on the surface, and the iterator then walks the prototype chain eagerly at construction time and reaches the Proxy regardless. This is contextual and never scored, and the reason is the same one that keeps every DevTools row unscored here: a person with their own DevTools open trips it exactly as an automation framework does, and from V8's side the two are the same code path. What it is good for is the thing the older Error.stack probe can no longer do — that vector was closed in V8 in May 2025, and this one was still open when it was published in March 2026.

How to resolve it

There is nothing to fix for a person browsing with DevTools open — the row is reporting a fact about your session, not a fault in your browser. If you are driving the page, the signal comes from the Runtime domain being enabled, so it is not addressable by patching anything in the page: an override in the main world runs long after the C++ inspector has already called the trap. Detach Runtime.enable when the run does not need it, or use a driver that keeps its evaluation in an isolated world and leaves the domain off. Note that suppressing the specific trap — refusing to preview objects with a Proxy in the chain — would be the same mistake this audit documents everywhere else: it patches the probe rather than the condition, and the condition is that a debugger is attached.

How automation gets caught, layer by layer

Read in the wild by

Sveba (research)

Publishes the technique and traces it through V8's own sources — the preview call in v8-console-message.cc, the surface-only Proxy guards in value-mirror.cc and debug-property-iterator.cc, and the spec-mandated trap invocation in keys.cc — with logs from an instrumented content_shell build.

console.groupEnd(Object.create(new Proxy({}, { ownKeys(){...} })))

How V8 Leaks Your Headless Browser's Identity

Rebrowser

Documents the older, now-patched half of the same question: the Error.stack accessor read on the inspector's error-formatting path, which this row exists to outlive.

index.js runtimeEnableTest

Rebrowser bot detector

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Automation surface

See all 43 checks in Automation surface
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Automation surface checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Automation surface — the family console-preview-proxy-trap belongs to.