user activation only ever appeared after a real input event
Check id activation-without-input
What an ordinary browser yields
Either no activation at all, or activation that arrived only after a trusted input event the page could see (severity: warn).
What a detector infers
The row above this one reads navigator.userActivation once and expects it to be true, because the audit is reached by pressing a button. Its own note concedes what a snapshot cannot do: a finger and a debugger both leave `true` behind. The distinguishing evidence is not the value but WHEN it changed relative to what the document received. Transient activation is granted only by a trusted input event, and a trusted input event delivered to this document is necessarily visible to a capture-phase window listener — so a page that watches from mount can assert something a snapshot cannot: activation went true while zero trusted input events had ever arrived. That is the page's own event log against the browser's own activation state, with no reference value in it and nothing that can rot. What it catches in practice is the Chrome DevTools Protocol: Runtime.evaluate carries a userGesture flag, and Playwright and Puppeteer set it, so page.evaluate() hands the document a real activation with no event behind it. Measured on the reference machine against a Chromium stealth build driven by Playwright: with no evaluate() call at all, 725 samples and zero activation observations; with evaluate(), 383 observations, every single one recorded at zero input events, the first arriving exactly when the first evaluate() fired. It is an oracle for CDP-driven automation in general rather than for any one product, which is what makes it worth having — the same reading identifies anything wrapping Playwright or Puppeteer, whatever the build underneath is called. One limitation is measured rather than assumed, and anyone relying on this row should know it: the detection is probabilistic, not deterministic. Across six otherwise identical runs against one unchanged browser, this row fired in three. The two other findings in those runs were stable at six out of six, so the variance belongs to this check alone. The reason is in the mechanism: what it catches is an evaluate that lands BEFORE the first real input, and whether a given script does that depends on whether the driver's internals happen to resolve a selector or read a property before the first click. Half the runs did. That makes this a row that can confirm automation but can never clear it — a quiet result means nothing was seen this time, not that nothing is there. It never goes the other way, though. The false-positive direction stays closed by the witness requirement, and a browser driven by a person produced no firing in any run.
How to resolve it
There is nothing to fix for a person browsing normally — their click is the event that grants the activation, so the two agree by construction. If you are driving this page, the finding is not about the browser build: page.evaluate() sets Runtime.evaluate's userGesture flag, and that is what granted the activation. Evaluate with userGesture:false, or drive the page through real input (CDP Input.dispatchMouseEvent) so the activation has an event behind it.
Read in the wild by
Kasada
Collects both halves of navigator.userActivation as separate signals — reading isActive as well as hasBeenActive is what makes the timing argument available to a detector at all.
user_activation_is_active, user_activation_has_been_active
Kasada ips.js teardown — the full 427-probe listEvery attribution traces to a published artifact. See the sources and their limits.
Nearby checks in Automation surface
- navigator.plugins is a real IDL collection, not a JavaScript object
plugins-idl-index-wraparoundA real navigator.plugins is a PluginArray — a C++-backed IDL collection — and its indexed getter runs the Web IDL unsigned-long conversion… - navigator.storage quota is the same in the window and a same-origin worker
storage-quota-worker-vs-mainnavigator.storage.estimate().quota describes one storage partition, and a same-origin worker shares that partition — a single QuotaManager… - performance.timeOrigin + performance.now() reconstructs Date.now()
clock-origin-coherenceA page has two clocks. performance.timeOrigin is the wall-clock moment its timeline began; performance.now() is the monotonic time elapsed… - the gesture that started this audit registered as a real user activation
user-activation-vs-interactionThis audit only runs when you press the Run button, and pressing a button is exactly what grants user activation — by pointer, by Enter or… - the clicks this page received were real clicks
interaction-click-trustEvent.isTrusted is set by the engine when it dispatches an event it generated itself, and it cannot be set from script - a constructed… - every keypress had a keydown behind it
interaction-keypress-without-keydownThe UI Events spec generates keypress as the DEFAULT ACTION of a keydown - it is not an independent event, it is what a keydown does when it… - key events named the key they carried
interaction-key-identity-populatedA keypress the engine generates names the character it is delivering in .key. - the dropdown change came from the browser, not from a script
interaction-select-change-trustThe same isTrusted argument as the click row, on the surface where the shortcut is most common.
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 Automation surface 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 Automation surface — the family activation-without-input belongs to.