[9] Don't log interaction from temporary gesture on storage access rejection
requestStorageAccess()'s rejection path silently logged user interaction — ITP's opener heuristic read that as consent and granted access without a prompt.
Rated High because the diff fixes a path where a no-gesture
requestStorageAccess()rejection silently logged user interaction that the ITP opener heuristic treats as consent, granting cross-site cookie access without any prompt.
enableTemporaryTimeUserGesture() constructed a UserGestureIndicator with the default ProcessInteractionStyle::Immediate, which logs synthetic user interaction as a side effect. Used on the rejection path so callers can still call window.open(), this fabricated interaction triggered requestStorageAccessUnderOpener().
Source/WebCore/dom/DocumentStorageAccess.cpp
Patch Details
A single-line change passes explicit UserGestureType::ActivationTriggering and ProcessInteractionStyle::Never, preserving gesture-gated capabilities while suppressing interaction logging. Regression test installs a popup with a cross-site iframe whose no-gesture requestStorageAccess() rejection no longer grants storage access.
Side-effecting helper used as a no-side-effect shim: a temporary UserGestureIndicator constructed for internal control-flow purposes silently emits user-interaction telemetry that downstream privacy heuristics treat as consent.
Background
UserGestureIndicator is an RAII helper that marks code as "processing a user gesture", enabling gesture-gated APIs. Its constructor takes a ProcessInteractionStyle: Immediate registers a user-interaction event with the page's domain as a side effect (feeding privacy telemetry); Never leaves gesture state visible without emitting telemetry. document.requestStorageAccess() lets a cross-site iframe request first-party cookie access. ITP / Resource Load Statistics tracks per-domain interaction; requestStorageAccessUnderOpener() automatically grants storage access if the origin has a recent recorded interaction and the popup has an opener.
Analysis
UserGestureIndicator does two logically separable things — mark a gesture scope, log user interaction — and the default constructor coupled them. Callers using the indicator for purely internal plumbing became interaction-logging callers by accident.
Exploit shape: arrange for tracker.example (recently interacted with, or primed to be ITP-prevalent) to be opened as a popup from publisher.example via window.open(). The popup loads a tracker.example page that embeds a cross-site iframe back to the tracker. The iframe's onload handler — no user gesture — calls document.requestStorageAccess(). The promise rejects, but the rejection path's temporary gesture logged a synthetic interaction. The opener heuristic then observed popup-with-opener + recent-interaction and silently granted storage access for the tracker origin going forward, with no prompt.
This vulnerability weakens the user-consent boundary protecting third-party storage access under ITP. The Storage Access API's invariant is that cross-site cookie access requires either an explicit prompt or recognized prior interaction; the bug made it bypassable through the rejection path of an API that should not produce consent.
Audit directions
- RAII helpers bundling telemetry side effects with control-flow semantics. Grep
Source/WebCoreandSource/WebKitfor everyUserGestureIndicator(construction and classify as "real user gesture" vs "synthetic / preservation gesture". For each synthetic site, verifyProcessInteractionStyle::Neveris passed. - Resource Load Statistics opener heuristic. Trace
requestStorageAccessUnderOpenerandlogUserInteractionfor non-click sources that feed it. Any non-click source is a candidate silent-grant primitive. - Privacy heuristics keyed on observable state that an API rejection can prime. Start with
ResourceLoadObserver,ResourceLoadStatistics, and theWebResourceLoadStatisticsStoreIPC entry points. - Sibling Storage Access helpers. Look near
consumeTemporaryTimeUserGesturecallers for other temporaryUserGestureIndicatorconstructions that need the same treatment.