← All issues

[9] Don't log interaction from temporary gesture on storage access rejection

Severity: High | Component: WebCore DOM (Storage Access API) | 5021f99

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

- m_temporaryUserGesture = makeUnique<UserGestureIndicator>(IsProcessingUserGesture::Yes, protect(m_document).ptr());
+ m_temporaryUserGesture = makeUnique<UserGestureIndicator>(IsProcessingUserGesture::Yes, protect(m_document).ptr(), UserGestureType::ActivationTriggering, UserGestureIndicator::ProcessInteractionStyle::Never);

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.

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.

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.

🔒

The interaction between an internal control-flow helper and a privacy-heuristic telemetry channel is dissected — including how a rejection path ended up producing the very signal it was rejecting against.

Subscribe to read more

🔒

Several reusable audit patterns covering RAII helpers with hidden telemetry, ITP opener heuristics, and adjacent synthetic-gesture sites — with concrete WebKit subsystems to grep.

Subscribe to read more