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.
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
- Function.toString not proxied (native code)
tostring-nativeThe check stringifies two functions and requires both to match /\{\s*\[native code\]\s*\}/: Function.prototype.toString itself (read as… - key native functions are untampered (toString + no prototype)
native-integrityThis runs a battery over roughly forty engine-owned callables — canvas, WebGL, navigator, iframe, shadow-DOM and fetch natives, plus the JS… - a fresh iframe (contentWindow) matches the main realm
iframe-coherenceThe check appends a hidden same-origin iframe to documentElement and reads its contentWindow — a brand-new JavaScript realm with its own… - Worker navigator matches the main thread
worker-navigator-coherenceA Blob-URL Worker is spawned and reports its own navigator.platform, navigator.hardwareConcurrency, navigator.webdriver and… - page-world natives that extensions commonly wrap
page-world-wrappersThis probe stringifies window.fetch and window.Request and asks whether each still renders as native code, then checks whether document… - nonexistent navigator properties return undefined
navigator-proxy-honeypotThis reads five navigator property names that exist on no browser — rml, ln, rnd, webdriverPatched and __clearcoteProbe — and additionally… - native methods enforce their C++ receiver check
native-brand-checksTwo native methods are invoked with their own bare prototype as the receiver… - iframe srcdoc/src/contentWindow live on the prototype
iframe-srcdoc-placementA fresh <iframe> is created with document.createElement and inspected for own property descriptors on srcdoc, src and contentWindow.
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.