iframe srcdoc/src/contentWindow live on the prototype
Check id iframe-srcdoc-placement
What an ordinary browser yields
A freshly created <iframe> has no own descriptors — srcdoc, src and contentWindow all resolve via HTMLIFrameElement.prototype.
What a detector infers
A fresh <iframe> is created with document.createElement and inspected for own property descriptors on srcdoc, src and contentWindow. On a stock engine these three resolve through HTMLIFrameElement.prototype, so a newly created element carries no own descriptors for them; the check fails if any per-element descriptor is present. This probes placement rather than value, and placement is the axis that engine-level spoofing preserves for free while JS interception cannot: controlling what loads into a fresh realm means installing accessors on the iframe's own srcdoc or src. That per-element machinery is precisely the countermeasure a stealth kit builds against cross-realm probes, and reading the srcdoc accessor's source text is a known detector behaviour. Severity is 'warn'; if the element cannot be created the check reports N/A.
How to resolve it
Stop installing per-element accessors on iframes to intercept what loads into new realms. If you need child realms to inherit a spoofed identity, apply the spoof at the engine level so it is present in every realm natively, leaving fresh iframe elements descriptor-free.
Read in the wild by
Kasada
Creates an <iframe> with document.createElement and reads Object.getOwnPropertyDescriptor(iframe, 'srcdoc') on that element, stringifying any get and set it finds — recording 'n/a' when there is no own descriptor, as on a stock engine — and scanning the recovered source text for the stealth-patch marker addContentWindowProxy. Separate probes capture the error message raised when reading .srcdoc, and the contentWindow geometry of a frame after srcdoc is assigned.
probes.txt: srcdoc_getter, srcdoc_setter, srcdoc_error_message, iframe_srcdoc_dimensions — all four in blog.txt's batch list (batch 2 slot 314: srcdoc_getter(fn_40118); batch 10 slot 415: srcdoc_setter(fn_30585); batch 1 slot 108: srcdoc_error_message(fn_132946); batch 7 slot 108: iframe_srcdoc_dimensions(fn_12124)). In the decompiled ips.js (2.txt): func_017d2a returns document.createElement("iframe"); func_029bef calls it, then Object.getOwnPropertyDescriptor(_7366,"srcdoc").get.toString()/.set.toString() with an "n/a" default, matching the result against ["abcd1234b","addContentWindowProxy"]; func_006d86: try { _2240.srcdoc } catch → errorMessage; func_004fc8 assigns ["srcdoc"] then reads contentWindow innerWidth/outerWidth/outerHeight/screenX/screenY/devicePixelRatio/pageXOffset/pageYOffset. Corroborated in a second script version (3.txt lines 32321, 40607, 45667, 47813).
CreepJS
CreepJS, an open-source research demo rather than a production detector, creates an iframe, assigns srcdoc to it, and reads contentWindow back without ever appending the element, recording the result in its stealth object as an indicator it names hasIframeProxy; it separately probes HTMLIFrameElement.prototype's contentWindow and contentDocument getters for proxy tampering, but never inspects srcdoc or src property descriptors.
src/headless/index.ts, in the `stealth` object: `hasIframeProxy: (() => { try { const iframe = document.createElement('iframe'); iframe.srcdoc = instanceId; return !!iframe.contentWindow } catch (err) { return true } })(),` (iframe is never appended to the DOM before contentWindow is read; no descriptor inspection). src/lies/index.ts: `searchLies(() => HTMLIFrameElement, { target: ['contentDocument','contentWindow'] })` — operates on prototype getters via `Object.getOwnPropertyDescriptor(proto, name).get`; the string 'srcdoc' does not appear in that file.
Every 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… - 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. - the global object still has every global a fresh realm has, in the same order
window-globals-vs-realmThe audit builds a fresh same-origin iframe and enumerates the own properties of both global objects, then asserts two things a browser… - 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 iframe-srcdoc-placement belongs to.