Skip to content
All fingerprint checksNative function integrity

Function.toString not proxied (native code)

Check id tostring-native

What an ordinary browser yields

Both probes return the native-code placeholder; pass.

What a detector infers

The check stringifies two functions and requires both to match /\{\s*\[native code\]\s*\}/: Function.prototype.toString itself (read as Function.prototype.toString.toString()), and navigator.permissions.query as a spot-check target. Per spec, a function implemented by the engine has no JavaScript source to return, so toString yields the NativeFunction placeholder; a function defined in JS returns its own source text. A userland layer that wraps a built-in has to keep the wrapper looking native, so it typically replaces Function.prototype.toString with a JS function that special-cases the wrapper — and that replacement returns its own source text, which is exactly what the first probe reads. The second probe covers the simpler case where permissions.query itself was swapped for a JS function. A detector that sees Function.prototype.toString reporting JS source infers that something in the page is actively rewriting the identity of its built-ins, which is a stronger signal than whatever the wrapper was concealing. This is a naive-tamper probe by design: a well-formed Proxy around the original is stringified by the engine as a NativeFunction and passes both probes, so proxied wrappers have to be caught by the other checks in the battery (descriptor shape, cross-realm reads).

How to resolve it

The contradiction comes from a JS-injection stealth layer proxying or wrapping Function.prototype.toString. Remove userland evasion shims that patch toString and let identity control happen at the engine level, where built-ins are genuinely native and stringify as such with no wrapper to hide. Note both probes are wrapped in safe(..., true), so a probe that throws is treated as a pass rather than a tell.

Why does Function.toString() reveal a hooked function?

Read in the wild by

Kasada

Kasada's ips.js carries a helper that takes a function as its argument, stringifies it with Function.prototype.toString, and returns whether the result contains "[native code]" — falling back to a typeof check if the stringification throws.

Kasada ips.js decompile, 1.txt:37974-37987 — func_028a4b(a0): `var _215 = a0; r7 = globalThis.Function; r8 = r7.toString; r6 = r8.call(_215); r2 = r6.indexOf("[native code]"); r7 = -1 !== r2; return r7;`. Its catch handler at 1.txt:37994-38003 falls back to `r5 = typeof _215; r4 = "function" == r5; return r4`, confirming the argument is the function under test. The same helper appears in the second script version at 3.txt:39187-39200, and in 2.txt:22008-22021 as func_014375 (where the decompiler mis-renders the call as `r7.apply(r7, _48)`). Registered in the helper table at 1.txt:279 (`_fn_27 = func_028a4b`) and 2.txt:130 (`_fn_17 = func_014375`). Corpus provenance: CONTEXT.md:40-42 (1.txt/2.txt/3.txt = IR + decompiles of Kasada ips.js).

PerimeterX / HUMAN

Tests engine-owned callables for the [native code] placeholder: Function.prototype.bind and Function.prototype.call via a regex byte-identical to this check's, and navigator.permissions.query via an anchored variant that stringifies through Function.prototype.toString.call. When permissions.query is not native it uploads the first 1024 characters of the wrapper's own source text; when Function.prototype.call is not native it uploads a hash of its source.

Deployed collector main.min.js (PerimeterX, iFood deployment; sha256 e042d5de83…a754e). Unanchored test, regex byte-identical to this check's: `function kz(t){return hR(t)===ie&&new RegExp("\\{\\s*\\[native code\\]\\s*\\}","").test(""+t)}` — applied to `Function[hQ(275)].bind` (hQ(275)='prototype') and, in `AU()`, to `hS[jb("RnVuY3Rpb24=")][jb("cHJvdG90eXBl")][jb(hQ(689))]` (= Function.prototype.call; hQ(689)='Y2FsbA=='), with `if(!kz(r))return kn(r+"")` where `kn` is a `(n<<5)-n+t.charCodeAt(e)` hash emitted as field "WGRtbh4Ca1s=". Separate anchored test: `function xC(t){try{return!!xE(t).match(new RegExp("\\{\\s*\\[native code\\]\\s*\\}$","m"))}catch(t){return!1}}` where `xE` = Function.prototype.toString.call; it gates `function xx(t){...xC(hU.permissions.query)||(t[hQ(373)]=hU[hQ(367)][hQ(368)][hQ(247)]()[hQ(332)](0,1024))...}` with hQ(367/368/247/332)='permissions'/'query'/'toString'/'substring' and hQ(373)='RlZzHAM2eS4=' (field key). String-table decodes corroborated by bug_report/sdk_drift_cases/2026-05-19_ifood/hQ_map.json; same construct recurs in the Walmart (ho/up) and Total Wine (kT) deployments under different mangling.

CreepJS

CreepJS — an open-source research demo rather than a production detector — stringifies each API against a table of six accepted `[native code]` forms and flags any mismatch as `failed toString`; it searches Function.prototype.toString first and uses that result, alongside Permissions.query, to gate its deeper proxy battery, surfacing the outcome as a named stealth signal, `hasToStringProxy`.

src/lies/index.ts: `function hasKnownToString(name) { return { [`function ${name}() { [native code] }`]: true, [`function get ${name}() { [native code] }`]: true, … } }`; `['failed toString']: (!hasKnownToString(name)[scope.Function.prototype.toString.call(apiFunction)] || !hasKnownToString('toString')[scope.Function.prototype.toString.call(apiFunction.toString)])`; `searchLies(() => Function, { target: ['toString'], ignore: ['caller', 'arguments'] })` — the first of 39 searchLies calls; `const detectProxies = (name == 'toString' || !!lieProps['Function.toString'] || !!lieProps['Permissions.query'])` gating the advanced proxy tests. src/headless/index.ts: `stealth: { …, hasToStringProxy: (!!lieProps['Function.toString']), … }`, feeding `stealthRating`.

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