the global object still has every global a fresh realm has, in the same order
Check id window-globals-vs-realm
What an ordinary browser yields
Every global present in a fresh realm is also present here, and the shared names appear in the same relative order. Extra globals on the page are normal and ignored.
What a detector infers
The audit builds a fresh same-origin iframe and enumerates the own properties of both global objects, then asserts two things a browser cannot violate on its own. First, subset: every platform global the pristine realm has must also exist here, because both realms are built from the same blueprint by the same browser. Additions are ignored outright — a real page has plenty, and so do extensions and wallets — but nothing legitimate REMOVES a platform global. Second, order — but on V8 only. V8 emits string-keyed own properties in insertion order and installs a realm's globals in a fixed order at setup, so the names shared by both realms must appear in the same relative sequence. SpiderMonkey does not work this way at all: Gecko resolves standard classes lazily, and the global's enumerate hook reports the still-unresolved names first and appends the already-resolved ones after, so a global sorts to the end merely by having been touched — order there reflects each realm's resolution history rather than a blueprint. Measured on stock Firefox 152 with a fresh profile and a bare page: 980 of 982 shared names out of position. Asserting order on every engine would not catch one extra spoof; it would invent a rule V8 happens to satisfy and then fail every Firefox, Tor Browser and LibreWolf user with a critical contradiction. The subset half stays unconditional, because it is engine-neutral and measured clean on every Gecko configuration. That second assertion is the interesting one, because it defeats the standard manoeuvre for hiding a global: `delete window.foo` followed by re-adding a convincing replacement. The replacement is appended, so it lands at the END of the property order, and the order itself becomes the evidence — no amount of making the VALUE look right can move it back to where the browser originally installed it. This is the largest single family in Kasada's deployed script: 44 of its 427 probes enumerate the global in both realms and sample length, five indices, and five slice indices from each. They can diff those samples against a server-side model of stock Chrome; this check has no baseline, so it asserts only what one realm can prove about the other. Integer-like keys are filtered first — those are the indexed frame accessors, and a page with subframes legitimately has more of them than a bare iframe.
How to resolve it
Do not delete or reassign globals to hide them — a fresh realm is built from the same blueprint as this one, so an absence here is visible against it, and re-adding a global puts it at the end of the property order where it never belonged. If a value must differ, change it at the browser layer, below JavaScript, so both realms derive it from one source rather than one realm being edited after the fact.
Read in the wild by
PerimeterX / HUMAN
Reads the globals HTMLPopupElement and NativeIOFile — two flag-gated experimental Chromium interfaces — off a hidden display:none iframe's contentWindow rather than the page's own window, flagging each in the payload if present. The same script reads showModalDialog off the page window, so the realm is chosen per probe rather than being a script-wide default.
Deployed PX collector, stample/ifood/source/main.min.js: `xI(){…t.a=xn("UGZYCbchcRyrzrag"),t[2]=xn("AngvirVBSvyr"),nG[t.a]&&(t[0]["FmYjbFMGJ14="]=!0),nG[t[2]]&&(t[0]["JVEQW2AxFGg="]=!0)}` — xn() is ROT13, so the names decode to 'HTMLPopupElement' and 'NativeIOFile'. nG is the hidden iframe's realm, from `nK(){(nH=hT.createElement("iframe")).style.display="none",…,nG=nH.contentWindow}` (hQ(166)="contentWindow"; `var nG` has no initializer), versus `hS=window` for the page's own — which xB() uses for its showModalDialog probe. ROT13 confirmed against observable output: xn("fubjZbqnyQvnybt")='showModalDialog', whose result rides hQ(390)="VQEgCxBhKjo=", and sample/1/decoded_payload_2.json shows `"VQEgCxBhKjo=": {"smd":{"ok":true,"ex":false}}`.
CreepJS
CreepJS, an open-source research demo rather than a production detector, enumerates the own property names of a hidden iframe's global instead of the page's, and separately flags window.chrome turning up among the last 50 keys of window — the position a re-added global lands in. It reads a single realm rather than diffing two; the cross-realm comparison is ours.
src/window/index.ts: `const win = PHANTOM_DARKNESS || window` / `let keys = Object.getOwnPropertyNames(win)`, plus the Gecko fixup `// if Firefox, remove the 'Event' key and push to end for consistent order` with `const firefoxKeyMovedByInspect = 'Event'`. PHANTOM_DARKNESS is the hidden iframe's contentWindow — src/lies/index.ts: `const { iframeWindow: PHANTOM_DARKNESS, div: PARENT_PHANTOM } = getPhantomIframe() || {}` (GHOST style: visibility:hidden, left:-10000px, appended via document.body.appendChild). src/headless/index.ts: `hasHighChromeIndex: (() => { const key = 'chrome'; const highIndexRange = -50; return (Object.keys(window).slice(highIndexRange).includes(key) && Object.getOwnPropertyNames(window).slice(highIndexRange).includes(key)) })()`
Kasada
Its single largest probe family: 44 of 427 probes enumerate the global object in BOTH the page and a fresh iframe, sampling the length plus five indices and five slice indices from each, then diffing the samples server-side.
win_own_props_idx0..4 / win_own_props_length / win_keys_idx0..4 / win_keys_length + the iframe_* twins
Kasada ips.js teardown — the full 427-probe listEvery attribution traces to a published artifact. See the sources and their limits.
Nearby checks in Native function integrity
- 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. - Element.attachShadow untampered (shape + closed/open behaviour)
shadow-dom-integrityThe check reads Element.prototype.attachShadow four ways and asserts they agree: its name is "attachShadow", its length (declared parameter… - Math.random / crypto.getRandomValues native and high-entropy
rng-integrityThe check verifies that Math.random is the engine's native generator and that its output is high-entropy. - iframe.contentWindow really is a separate realm, and the one the browser says it is
contentwindow-realm-identityThis is the check that guards the others. A large part of this audit — the global property diff, all six capability surfaces, the brand…
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 window-globals-vs-realm belongs to.