Skip to content
All fingerprint checksNative function integrity

navigator props resolve via native prototype getters

Check id navigator-descriptors

What an ordinary browser yields

No own descriptors on navigator; all five resolve via native getters on Navigator.prototype.

What a detector infers

For deviceMemory, hardwareConcurrency, language, languages and platform, the check reads the property descriptor twice. First Object.getOwnPropertyDescriptor(navigator, prop): in a stock browser this returns undefined, because these are accessors defined on Navigator.prototype, not own data properties on the instance. Any own descriptor is a tell — a `value` field means someone assigned a plain data property, and a `get` field is only accepted if that getter itself stringifies as [native code]. If no own descriptor exists, it falls back to Navigator.prototype and fails only if a descriptor is present without a `get`, i.e. the prototype accessor was replaced by a data property. The mechanism is reliable because the shape of the descriptor is structural: Object.defineProperty(navigator, 'platform', {...}) cannot reproduce a native prototype accessor, and a plain `navigator.platform = ...` silently fails against a getter-only property, so any value that did land is provably userland.

How to resolve it

Drop the Object.defineProperty overrides on navigator (or the library installing them) and set these values at the engine/binary level instead, so the prototype accessor itself returns the intended value and the descriptor shape stays indistinguishable from stock.

Why does navigator.webdriver reveal automation?

Read in the wild by

Kasada

Kasada's collector performs this same descriptor-shape read: it calls Object.getOwnPropertyDescriptor on the navigator instance and on Navigator.prototype, then branches on whether the descriptor is undefined, whether it carries a `get`, and what that getter stringifies to — reading webdriver, plugins and languages rather than the five properties this check sweeps.

2.txt:45547-45550 — r10 = globalThis["window"]; r8 = r10["navigator"]; r4.getOwnPropertyDescriptor(r8, "webdriver"); if present, r2["get"] is matched against the regex built at 2.txt:45544: "function\s*\(\)\s*{\s*return\s*(?:false|undefined);?\s*}|\(\)\s*=>\s*(?:false|undefined)". Also 2.txt:41841-41843 — r9 = globalThis["navigator"]; r10 = r9["__proto__"]; globalThis.Object.getOwnPropertyDescriptor(r10, "languages"), whose .get.toString() is tested against "opts.languages", "() => ['en-US', 'en']" and "() => [language]". Also 2.txt:9021-9027 — getOwnPropertyDescriptor(<window>["navigator"], "plugins") tested `_2439 === undefined`, then `_2439["get"].toString()` (repeated at 2.txt:12036-12043).

CreepJS

CreepJS, an open-source research demo rather than a production detector, flags an own property descriptor on the navigator instance as a lie: its `failed undefined properties` test calls Object.getOwnPropertyDescriptor(navigator, name) and treats any non-undefined result as a tell, across a named property list that includes deviceMemory, hardwareConcurrency, language, languages and platform.

creepjs, src/lies/index.ts — lie test: `['failed undefined properties']: (!!obj && /^(screen|navigator)$/i.test(objName) && !!(Object.getOwnPropertyDescriptor(self[objName.toLowerCase()], name) || (HAS_REFLECT && Reflect.getOwnPropertyDescriptor(self[objName.toLowerCase()], name))))` — and the call that feeds it: `searchLies(() => Navigator, { target: ['appCodeName','appName','appVersion','buildID','connection','deviceMemory','getBattery','getGamepads','getVRDisplays','hardwareConcurrency','language','languages','maxTouchPoints','mimeTypes','oscpu','platform','plugins','product','productSub','sendBeacon','serviceWorker','storage','userAgent','vendor','vendorSub','webdriver','gpu'] })`. searchLies filters Navigator.prototype's property names to that target list and routes each through the lie tests, where objName 'Navigator' matches /^(screen|navigator)$/i so self['navigator'] is the navigator instance.

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

Nearby checks in Native function integrity

See all 13 checks in Native function integrity
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 Native function integrity 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 Native function integrity — the family navigator-descriptors belongs to.