How bot detection works — and why Clearcote patches the engine
The usual approach patches navigator.webdriver, spoofs the WebGL vendor, and overridesnavigator.plugins from script. Detectors still flag it — and the reason is structural, not one more property left uncovered.
A JavaScript spoof self-reveals
A JS spoof is a function standing where a native one belongs. A detector sets the returned value aside and interrogates whether the thing returning it is native:
| The tell | Why it catches a JS spoof |
|---|---|
toString self-reveal | A native method stringifies to function get vendor() { [native code] }; an override stringifies to its own source — one .toString() catches it. |
Descriptor / hasOwnProperty | getOwnPropertyDescriptor exposes redefined props, and hasOwnProperty('toString') returns true on a tampered function where a native one returns false. |
Wrong-this TypeError | Native getters throw a specific TypeError on the wrong receiver; a naive shim stays quiet, and the silence is the signal. |
| Realm re-acquisition | A detector grabs a pristine Function.prototype.toString from a fresh iframe or Web Worker and turns it on your getter — a different realm from your main-world patch. It returns your source. Caught. |
Clearcote has no such layer. The getter for navigator.vendor is the C++ getter: it reports [native code] because it is native code, identical across every realm — main frame, iframe, and worker. There is no JavaScript hijacking to detect.
The three layers of bot detection
Modern anti-bot systems read three structurally different surfaces, in three separate places. One tool rarely fixes all three:
| Layer | The tells | Where the fix lives | Clearcote |
|---|---|---|---|
| A · driver / binary | cdc_ ChromeDriver vars, the WebDriver protocol surface | Drive raw CDP, skip chromedriver | ✅ a plain Chromium binary — no driver artifacts |
| B · CDP side-effects | Runtime.enable leaks, injected init-scripts, main-world execution, automation-default viewport | The control / CDP-client layer | ✅ launch defaults hold these back (isolated worlds, non-default viewport) |
| C · fingerprint surface | canvas, WebGL, audio, fonts, navigator, TLS — across main frame, iframes, workers | The engine (C++), because JS overrides self-reveal | ✅ this is Clearcote |
The thing that matters most: no spoof-vs-real seam
Because the controls live in the engine, the JavaScript a page sees and the network handshake underneath it come from one real Chromium. There is no spoofed-JS-over-real-TLS seam for a cross-check to catch — the exact failure mode that gives injection-based tools away. One --fingerprint seed produces a single, internally consistent machine across canvas, WebGL, audio, fonts, locale, hardware — and the TLS / HTTP-2 fingerprint underneath.
And when the noise itself is the tell, switch it off — canvas/WebGL/audio return their natural values while the identity spoof stays on:
await launch({ fingerprint: "u1", fingerprintNoise: false }); // identity on, per-site farbling offThis is why Clearcote patches the C++ engine instead of injecting JavaScript. See Fingerprint flags for what you control, and Architecture for how the engine keeps every signal coherent together.