Skip to content
Documentation menu

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 tellWhy it catches a JS spoof
toString self-revealA native method stringifies to function get vendor() { [native code] }; an override stringifies to its own source — one .toString() catches it.
Descriptor / hasOwnPropertygetOwnPropertyDescriptor exposes redefined props, and hasOwnProperty('toString') returns true on a tampered function where a native one returns false.
Wrong-this TypeErrorNative getters throw a specific TypeError on the wrong receiver; a naive shim stays quiet, and the silence is the signal.
Realm re-acquisitionA 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:

LayerThe tellsWhere the fix livesClearcote
A · driver / binarycdc_ ChromeDriver vars, the WebDriver protocol surfaceDrive raw CDP, skip chromedriver✅ a plain Chromium binary — no driver artifacts
B · CDP side-effectsRuntime.enable leaks, injected init-scripts, main-world execution, automation-default viewportThe control / CDP-client layer✅ launch defaults hold these back (isolated worlds, non-default viewport)
C · fingerprint surfacecanvas, WebGL, audio, fonts, navigator, TLS — across main frame, iframes, workersThe engine (C++), because JS overrides self-revealthis 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:

javascript
await launch({ fingerprint: "u1", fingerprintNoise: false });   // identity on, per-site farbling off
This 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.