Skip to content
All fingerprint checksNative function integrity

key native functions are untampered (toString + no prototype)

Check id native-integrity

What an ordinary browser yields

All present targets stringify as native and expose no .prototype; pass.

What a detector infers

This runs a battery over roughly forty engine-owned callables — canvas, WebGL, navigator, iframe, shadow-DOM and fetch natives, plus the JS core (Object.*, Reflect.*, Function.prototype bind/call/apply, JSON.stringify and the constructors) — and applies up to three tests to each. First the [native code] stringification marker. Then, on V8 only, byte-exact equality against an engine-derived template of what a native function's source text must look like: the template is self-validated against just three constructors, and the live audit corpus showed that exactness false-positiving on 100% of genuine Firefox and Safari (SpiderMonkey/JavaScriptCore format some natives differently), so off V8 the marker test alone carries the check — wrappers and shims are still caught, formatting quirks are no longer punished. Last, `'prototype' in fn`: built-in methods and accessors are non-constructors and per spec carry no .prototype property, whereas an ordinary `function(){}` used as a wrapper gets one automatically — a shim that returns a convincing string from toString can still be given away by a .prototype the original never had. Targets that aren't functions in this browser are skipped rather than failed, so an unsupported API doesn't count against the result.

How to resolve it

Each entry named in the failure detail is a built-in that has been replaced or wrapped by a JavaScript function. Replace the wrapper approach with engine-level control of the underlying value. Note what this battery does and does not cover: an arrow function or a method-shorthand avoids the .prototype tell but still returns its own source from toString, while a Proxy around the original passes both probes — the engine stringifies a proxied callable as a NativeFunction and forwards the 'prototype' lookup to the native target. Proxied wrappers are surfaced by the descriptor-shape and cross-realm checks instead.

How do anti-bot systems detect automation?

Read in the wild by

PerimeterX / HUMAN

Stringifies engine-owned callables and ships the result two ways: a [native code] regex test for setTimeout, setInterval, Function.prototype.bind and getComputedStyle, and a 32-bit hash of the function source for Object.prototype.toString, Object.getOwnPropertyDescriptor, localStorage.setItem, documentElement.dispatchEvent and console.log.

main.min.js: `function kz(t){return hR(t)===ie&&new RegExp("\\{\\s*\\[native code\\]\\s*\\}","").test(""+t)}` (ie="function") — a boolean native-code test — applied as `t["YQ1UByRqUzE="]=kz(hS.setTimeout)`, `t["KxseEW16HCU="]=kz(hS.setInterval)`, `t["b19aVSk/X2c="]=kz(Function[hQ(275)].bind)`, `t["eEQNDj0lCzU="]=hT[hQ(826)]&&kz(hT[hQ(826)].getComputedStyle)` (hQ(275)='prototype', hQ(826)='defaultView'). Separately, in `function Ck(t)`: `kD(t,"PSkIY3tJCVg=",(function(){return BS(hS.console[hQ(753)])}),"")`, `kD(t,hQ(755),(function(){return BS(Object.prototype.toString)}),"")`, `kD(t,"b19aVSkzXW4=",(function(){return BS(Object[hQ(698)])}),"")`, `kD(t,hQ(767),(function(){return BS(hS.localStorage[hQ(419)])}),"")`, `kD(t,hQ(765),(function(){return BS(hT.documentElement[hQ(766)])}),"")` — where `function BS(t){if(hR(t)!==hW)return kn(t)}` and `function kn(t){t=""+t;for(var n=kk,e=0;e<t.length;e++){n=(n<<5)-n+t.charCodeAt(e),n|=0}return kJ(n)}` (kk=0; `kJ` renders the uint32 as hex), i.e. a hash of the function source. hQ(753)='log', hQ(698)='getOwnPropertyDescriptor', hQ(419)='setItem', hQ(766)='dispatchEvent'.

CreepJS

CreepJS, a research demo rather than a production detector, pairs the native-code stringification with the exact same `'prototype' in fn` non-constructor test, and applies it to targets including Permissions.query, CanvasRenderingContext2D.getImageData and HTMLIFrameElement.contentWindow.

src/lies/index.ts:200 `['failed "prototype" in function']: 'prototype' in apiFunction,` — an adjacent key, in the same lies object and on the same apiFunction, to the native-code test at :196-199 `['failed toString']: (!hasKnownToString(name)[scope.Function.prototype.toString.call(apiFunction)] || …)`. Targets reaching that object via searchLies: :726 `searchLies(() => Permissions, { target: ['query'] })`; :494 `searchLies(() => CanvasRenderingContext2D, { target: ['getImageData', 'getLineDash', 'isPointInPath', …] })`; :626 `searchLies(() => HTMLIFrameElement, { target: ['contentDocument', 'contentWindow'] })`. Accessor targets are covered too: :413-419 extracts the getter via `Object.getOwnPropertyDescriptor(proto, name).get` and passes it through the same queryLies. Also src/headless/index.ts:136-138: `if ('prototype' in chrome.runtime.sendMessage || 'prototype' in chrome.runtime.connect) { return true }`.

Kasada

Spends 23 probes on the language builtins rather than the DOM: the natives a spoof rewrites the DOM WITH.

prop_recorder_to_string, prop_recorder_bind, prop_recorder_call, prop_recorder_apply, prop_recorder_new_function, prop_recorder_own_descriptors, prop_recorder_proxy, prop_recorder_error_constructor, …

Kasada ips.js teardown — the full 427-probe list

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 native-integrity belongs to.