Skip to content
All fingerprint checksNative function integrity

a fresh iframe (contentWindow) matches the main realm

Check id iframe-coherence

What an ordinary browser yields

Iframe navigator values equal the main realm's, and its natives stringify as native.

What a detector infers

The check appends a hidden same-origin iframe to documentElement and reads its contentWindow — a brand-new JavaScript realm with its own fresh copies of navigator, Function and HTMLCanvasElement. It compares platform, hardwareConcurrency, webdriver and userAgent against the top realm, and additionally stringifies permissions.query and HTMLCanvasElement.prototype.toDataURL using the iframe's own Function.prototype.toString, requiring both to contain [native code]. This is decisive because a JS patch runs against the objects of the realm it executed in; a realm created afterwards is populated directly by the engine and is untouched, so main-thread-scoped spoofing produces a contradiction between two realms of the same page that a real browser can never produce. Using the iframe's own toString also defeats a patched toString in the parent. The check reports N/A (unscored) if contentWindow is unavailable or the probe throws.

How to resolve it

The listed fields are being overridden in the top realm only. Move the values into the engine so every realm is born with them, rather than patching window.navigator after load. Frameworks that inject an init script per frame can narrow the gap, but a synchronously created about:blank iframe can be read before any per-frame script has run, and the wrapper artifacts this check does stringify — permissions.query and canvas.toDataURL, read through the iframe's own Function.prototype.toString — remain.

Cross-realm coherence: why a worker must agree with the page

Read in the wild by

PerimeterX / HUMAN

Appends a display:none iframe and keeps its contentWindow as a fresh realm: it re-reads navigator.userAgent and navigator.webdriver through that realm into EV2, borrows the iframe's Object.getOwnPropertyDescriptor to read and hash the main realm's Navigator.prototype 'webdriver' descriptor, and stringifies the iframe's own navigator.permissions.query using the iframe's Function.prototype.toString.

PerimeterX main.min.js (ifood build, /root/re/PerimeterX_RE/stample/ifood/source/main.min.js): `function nK(){try{(nH=hT.createElement("iframe")).style.display="none",nH.onload=function(){nG=nH[hQ(166)],nH.onload=void 0},hT.body.appendChild(nH),nG=nH.contentWindow}catch(t){}}` (hQ(166)='contentWindow'; same function as jE/jA in the walmart build and od/nZ in totalwine). Realm reads: `var n=CH(BM,nG);n&&(t["EwNmCVZkYjs="]=n[BK],t["W0suQR4sKHo="]=!!n[BI],kD(t,"BFAxGkI9Oi8=",(function(){var t=n[BJ][hQ(209)](this,Object.getPrototypeOf(hU),BL);if(t)return kn(""+(t[hQ(279)]||"")+(t.value||""))}),""))` where `BM=[BI,BJ,BK]`, BI=jb("bmF2aWdhdG9yLndlYmRyaXZlcg==")='navigator.webdriver', BJ=jb("T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcg==")='Object.getOwnPropertyDescriptor', BK=jb(hQ(723)) with hQ(723)='bmF2aWdhdG9yLnVzZXJBZ2VudA=='='navigator.userAgent', BL=jb("d2ViZHJpdmVy")='webdriver', hQ(209)='call'; `function CH(t,n){…var i=n,o=t[h];…var c=o[hQ(147)](".");for(var a in c){if(c[hQ(145)](a))i=i[c[a]]}…}` walks each dotted path against nG. Observed values (bug_report/sdk_drift_cases/2026-05-19_ifood/all_fields_map.json): EV2 "EwNmCVZkYjs=" line 7808 = "Mozilla/5.0 (Windows NT 10.0; Win64; x6…", EV2 "W0suQR4sKHo=" line 7809 = boolean false, EV2 "BFAxGkI9Oi8=" line 7810 = "90e65465"; all three keys present in ev2_template.json. Permissions stringification: `function xs(){…void 0!==hU.permissions&&void 0!==hU[hQ(367)][hQ(368)]&&(t.a=xN(nG,nG.navigator[hQ(367)][hQ(368)]),t.a&&(t[0]["Dz96dUpff0A="]=t.a))…}` with hQ(367)='permissions', hQ(368)='query', and `function xN(hM,hR){…dj=hM.Function.prototype.toString[hQ(hU+274)](hR),dj.indexOf(xn(hQ(407)))…}`. Provider identity from the file itself: `_pxAppId`, `_pxJsClientSrc`, `_pxUuid`, regex `perimeterx\.net|a\.pxi\.pub|px-cdn\.`, PX#### tokens.

CreepJS

CreepJS, an open-source research demo rather than a production detector, reaches into a hidden same-origin iframe realm for one narrow purpose: it stringifies each API under test with that realm's own Function.prototype.toString instead of the page's, so a patched toString in the top realm cannot launder the result — and Permissions.prototype.query and HTMLCanvasElement's methods are both among the ~39 surfaces it runs this on. The APIs themselves are still read from the page realm; the iframe supplies only the stringifier.

src/lies/index.ts (abrahamjuliot/creepjs, master): `const { iframeWindow: PHANTOM_DARKNESS, div: PARENT_PHANTOM } = getPhantomIframe() || {}` (L462) is passed as `scope` by `getPrototypeLies(PHANTOM_DARKNESS as Window & typeof globalThis)` (L818). `scope` is then used in exactly two places in the 953-line file — L197-198: `['failed toString']: (!hasKnownToString(name)[scope.Function.prototype.toString.call(apiFunction)] || !hasKnownToString('toString')[scope.Function.prototype.toString.call(apiFunction.toString)])`. The APIs under test are resolved in the page realm via thunks, e.g. `searchLies(() => Permissions, { target: ['query'] })` (L726) and `searchLies(() => HTMLCanvasElement)` (L610). Iframe nesting is Blink-only: `getBehemothIframe` opens with `if (!IS_BLINK) return win` (L49).

Kasada

Its central technique: 172 of its 427 probes are the same read performed in both the main window and a throwaway iframe, then diffed.

the window_* / iframe_* probe pairs — user_agent, platform, webdriver, screen_*, timezone_offset, device_memory, hardware_concurrency, plugins, …

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 iframe-coherence belongs to.