Medium CVSS 6.5 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionA maliciously crafted webpage may be able to fingerprint the user
ComponentWebCore Rendering
Bug ClassLogic Error
Tracker262337
Fix commitf0fba73ace0f (WebKit/WebKit) +5/-1
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedEmilio Cobos of Mozilla
Disclosed2024-05-13

Background

:visited privacy protections
Browsers allow limited :visited styling but restrict readback so pages cannot detect which links a user has visited.
visitedDependentColor + paintBehavior
A style lookup that returns the visited or unvisited color; the paintBehavior tells it whether visited styles must be ignored for the current paint.
SVG/CSS filter side channel
A filter processes rendered pixels; if visited-dependent color enters the filter, the observable output can leak visited state.

Root Cause Analysis

This fixes a browsing-history disclosure (visited-link detection) via SVG/CSS filters applied to hyperlinks. WebKit lets :visited links be styled but tightly restricts what a page can observe about visited state to prevent history sniffing; painting code must ignore visited-dependent styles in contexts where the result could be read back. Applying an SVG filter to a hyperlink, however, could leak the visited state through the link’s BACKGROUND color: InlineBoxPainter::paintDecorations obtained the background color with style.visitedDependentColor(CSSPropertyBackgroundColor) WITHOUT passing the current paint behavior, so it used the visited-dependent background even when painting into a filter pipeline whose output the page can observe (per the averaging/filter attack described in arXiv:2305.12784). A page could thus set different a:visited background colors, apply an SVG filter, and infer from the filtered output whether a given link had been visited.

The fix threads the paint behavior into the lookup: style.visitedDependentColor(CSSPropertyBackgroundColor, m_paintInfo.paintBehavior), so when the paint behavior indicates visited styles must be ignored (e.g. while producing filter input), the unvisited background color is used instead of the visited-dependent one. This is a follow-up to an earlier fix (266683@main) that handled the text color; this commit closes the same leak for background-color.

The restored invariant is that visited-dependent background color is not used when painting in a context that could expose it, so filters cannot recover visited state. The regression test gives a:link and a:visited different background colors and checks the filtered rendering does not reveal visited state.

Key insight
paintDecorations looked up the visited-dependent BACKGROUND color without honoring the paint behavior, so filters could read visited state through the background; passing paintBehavior makes the painter ignore visited styles where the result is observable (mirroring the earlier text-color fix).

Attack Path

  1. Style visited backgrounds Define a:link and a:visited with different background-color values for target links.
  2. Apply an SVG filter Apply an SVG/CSS filter to the hyperlink so the visited-dependent background is drawn into the filter pipeline.
  3. Observe the filtered output Read back the filtered rendering (per the averaging attack) to distinguish the visited vs unvisited background.
  4. Infer history Deduce whether each link was visited, leaking the user’s browsing history.

Impact Assessment

A browsing-history disclosure (privacy) issue in the WebContent process with no memory corruption: SVG/CSS filters on hyperlinks could leak visited-link state via the background color. It reveals which sites a user has visited — a meaningful privacy leak — but does not by itself corrupt memory or execute code.

Changed Functions

FunctionChangeNotes
InlineBoxPainter::paintDecorations
Source/WebCore/rendering/InlineBoxPainter.cpp
modified Passes m_paintInfo.paintBehavior to style.visitedDependentColor(CSSPropertyBackgroundColor, ...) so visited-dependent background is ignored when painting in a context (e.g. filter input) that could expose visited state.

Files Changed

  • LayoutTests/css3/filters/filter-visited-links-expected.html
  • LayoutTests/css3/filters/filter-visited-links.html
  • Source/WebCore/rendering/InlineBoxPainter.cpp

Audit Directions

  • visitedDependentColor call sites
    Grep for style.visitedDependentColor(…) that omit a paintBehavior argument in painting code (backgrounds, borders, shadows, decorations) reachable inside filters/readback.
  • Visited-state readback surfaces
    Review filter, canvas-drawImage, and paint-into-image paths for use of visited-dependent styles that could leak history.
diff --git a/LayoutTests/swipe/resources/swipe-test.js b/LayoutTests/swipe/resources/swipe-test.js
index a32ba832493c..132f6c9c6c7f 100644
--- a/LayoutTests/swipe/resources/swipe-test.js
+++ b/LayoutTests/swipe/resources/swipe-test.js
@@ -46,6 +46,8 @@ async function startSlowSwipeGesture()
     if (!window.eventSender)
         return;
 
+    log("startSlowSwipeGesture");
+
     await UIHelper.ensurePresentationUpdate();
 
     // Similar to uiController.beginBackSwipe(), but with a gap between events to allow
diff --git a/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt b/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
index 8215851db5ff..fd5b55d8f959 100644
--- a/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
+++ b/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
@@ -1,4 +1,5 @@
 Swipe target
+startSlowSwipeGesture
 didBeginSwipe
 completeSwipeGesture
 willEndSwipe
diff --git a/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt b/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
index 6bd807db86af..fd5b55d8f959 100644
--- a/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
+++ b/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
@@ -1,5 +1,5 @@
 Swipe target
-startSwipeGesture
+startSlowSwipeGesture
 didBeginSwipe
 completeSwipeGesture
 willEndSwipe
diff --git a/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt b/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
index e7a0ac32652b..975b6f62289c 100644
--- a/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
+++ b/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
@@ -1,3 +1,4 @@
 Swipe target
+startSlowSwipeGesture
 completeSwipeGesture
 
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker.