[7] label-forwarded clicks promoted to isTrusted=true via dispatchSimulatedClick
Severity: Medium | Component: WebCore DOM event dispatch | fc1ef83
Rated Medium because the diff fixes a deterministic Event.isTrusted spoofing path via <label> forwarding; the immediate concrete consequence is bypass of the haptic-feedback trusted-event gate on <input type=checkbox switch>, with broader impact bounded by which other features gate on isTrusted for clicks on label-associated controls.
288403@main ensured that haptic feedback for <input type=checkbox switch> required user activation. However, it is also desired that haptics are only triggered for trusted events. This goal can currently be bypassed by calling click() on a label associated with the input. The underlying issue is that Element::dispatchSimulatedClick unconditionally sets SimulatedClickSource::UserAgent. Fix by specifying SimulatedClickSource::Bindings if there is an underlying event and it is untrusted.
Source/WebCore/dom/Element.cpp
bool Element::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions)
{
- return simulateClick(*this, underlyingEvent, eventOptions, visualOptions, SimulatedClickSource::UserAgent);
+ auto simulatedClickSource = [&] {
+ if (!underlyingEvent)
+ return SimulatedClickSource::UserAgent;
+
+ return underlyingEvent->isTrusted() ? SimulatedClickSource::UserAgent : SimulatedClickSource::Bindings;
+ }();
+
+ return simulateClick(*this, underlyingEvent, eventOptions, visualOptions, simulatedClickSource);
}
LayoutTests/fast/forms/label/label-click-event-dispatch-untrusted.html
+<input id="input" type="checkbox">
+<label id="label" for="input"></label>
+<script>
+input.addEventListener("click", (event) => {
+ shouldBeFalse("event.isTrusted");
+});
+label.click();
+</script>
Patch Details
Element::dispatchSimulatedClick now derives the SimulatedClickSource from the trust state of the underlying event: UserAgent only when the underlying event is itself trusted, Bindings otherwise. When no underlying event is present (the path used by Element::click() itself), the behaviour is unchanged. A layout test verifies that label.click() produces an untrusted click on the associated input, a WPT expectation moves from FAIL to PASS, and SwitchInputTests.mm asserts haptic feedback fires on a real user click but not on a label-forwarded programmatic click following a user gesture.
Failure to propagate the trust bit of an underlying event across an internal event-forwarding boundary, allowing an untrusted event to be relabeled as trusted on the forwarded target.
Background
Event.isTrusted is a boolean flag that is true only when the event was created by the user agent in response to user input. Many security- and privacy-sensitive APIs (autoplay, fullscreen, clipboard, haptics) gate on this flag in addition to or instead of user activation. SimulatedClickSource is an internal WebKit enum: UserAgent causes the synthesised click to be dispatched with isTrusted = true, while Bindings causes it to be dispatched with isTrusted = false. Element::dispatchSimulatedClick(Event* underlyingEvent, ...) is the helper used to deliver a synthesised click on behalf of another event — its primary in-tree caller is the <label> element, which forwards clicks on the label to its associated form control. Element::click() (the IDL-exposed method) is a separate path that calls simulateClick directly with no underlying event and is by design always untrusted. User activation and event trust are independent signals: activation tracks recent interaction at the document level, while isTrusted tracks the provenance of an individual event object. A trusted-event check is strictly stronger than a user-activation check, because activation can persist briefly after a real gesture.
Analysis
Element::dispatchSimulatedClick hard-coded SimulatedClickSource::UserAgent for every simulated click forwarded from a label. The trust-laundering chain is: untrusted JS-initiated label.click() → HTMLLabelElement forwards the click to its associated control via dispatchSimulatedClick(underlyingEvent=<untrusted click>) → the simulated click on the <input type=checkbox switch> is dispatched with SimulatedClickSource::UserAgent → downstream consumers (notably the haptic-feedback gate from 288403@main, which requires a trusted event in addition to user activation) accept the event as trusted.
Aaaa Aaa Aaaaaaaa Aaa Aaaaa Aa Aaaaaaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aa Aaaaaaaaaa Aaaa a Aaaaaaaaaa Aaa Aaaaaa a Aaaaaaa Aa Aaa Aaaaaaa Aaaa Aaaaaaaa a Aaaa Aaaa Aaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaa a Aaaaa Aaaa Aaaaaaaaaaaaa Aaaaa a Aaaa Aaaaaaa Aaa Aaaaaaaaa Aaa Aaaaa Aaaa Aaaaaaaaaa a Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaaaa Aa Aaaaaaaaa Aaaaa Aaaaa Aa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaa Aaa Aaaaaaaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa a Aaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaa Aaa Aaaaa Aaaaa Aaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaa Aaaaaaa Aaaa Aaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaa Aaaaa Aa Aaaa Aa Aaaaaaaa Aaaaa Aaaaaaa Aa Aaaaa Aaaaa Aaaa Aaaa Aa Aaaaaaaaaaa Aaa Aaaaa Aaaaaa Aa Aaaaa Aaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaa Aa a Aaaaaaaaa Aaaaa Aaaa a Aaa Aaaaaa Aaa Aaaa Aa Aaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaa Aaaaaaaa Aaaaaaaaaa a Aaaaaaa Aaa a Aaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaa Aa Aa Aaaaaaaaaa a Aaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaa Aaaaaa Aaaa Aaa Aaaaaaaaaa Aaaaaaa Aaaaa Aaaa Aaaa Aaaa Aa Aaaaaaaaaaa Aaaa Aaaa Aaaaa Aa Aa Aaaaaaaaaa Aaaaaa Aa Aaaaa Aaaaaaaa Aa a Aaaaaaa Aaaa Aaaaaaaa Aaaaaa Aaaaaaaaaaa a Aaa Aaaaa Aaaa Aaaaaaa Aaaaaa
🔒Walks through how a script-initiated event can cross an internal forwarding boundary inside WebCore and emerge with elevated trust — and what realistic policy gates that lets a page bypass from ordinary web content.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaa Aaaaaaa Aaaa Aaaa Aaaa Aaaaaaaaaaa a Aaa Aaaaa Aaaa Aaaaaaa Aaaaa Aaa Aaaaaaa a Aaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaa a Aaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaa Aaaaaa Aa Aa Aaaaaaaaaaaaaaaaa Aa a Aaaaaaaaa Aaa Aaa Aaaa Aaaa
a Aaaaaaaaaa Aaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aa Aaaa Aaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaaaa Aaaaaa Aa Aaaaa Aaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaa Aa Aaaaaa Aaaaaaa Aaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaa Aaaa Aaaa Aaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaa
a Aaaaaaaaaaaaa Aaaaaa Aaaaaaaa Aa Aa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaa a Aaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaa Aaaa Aaaaa Aaaaaaaa
a Aaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaaaaa Aa Aaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaa Aaaaaaaaaaa Aaaa Aa Aaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aa Aaaaaaaaa Aaaa Aaa Aaaaa Aaaa Aaaaaaaaa Aaaaaaaaa Aaaaaa Aaaaa Aaaaaa Aaaaaaaaa Aaaaaaaa Aa Aaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaa Aaa Aaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaa Aaaaa
🔒Several reusable audit patterns for event-trust laundering across WebKit's synthesized-event helpers, with concrete grep targets and subsystems to start with.
Subscribe to read more