Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactObservable discrepancy in SVG
DescriptionObservable discrepancy in SVG
ComponentSVG
Bug ClassLogic Error
Tracker514489101
Fix commit07625009a5ee (chromium/src) +339/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Changed Functions

FunctionChangeNotes
promise_test
third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
modified
promise_test
third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
modified

Files Changed

  • third_party/blink/renderer/core/paint/svg_object_painter.cc
  • third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
  • third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
  • third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html
From 07625009a5eefe24a3511b87fee9142e4d7a71a3 Mon Sep 17 00:00:00 2001
From: Stephen Chenney <schenney@chromium.org>
Date: Wed, 12 Aug 2026 13:22:26 -0700
Subject: [PATCH] [HiC] Disallow non-subtree SVG paint resources

SVG Paint Resources may use visited link colors, or the
<pattern> element may contain arbitrary DOM content, including
forms with autofill. When defined outside the canvas subtree
these elements are not marked as being inside the subtree, which
defeats various privacy preserving measures. So disallow the
use of resources from outside the canvas subtree when inside the
canvas subtree.

Add tests and start to re-organize the privacy tests to make it
easier to see what areas we are testing.

A bug has been filed to verify the behavior of SVG <use> elements.

Fixed: 514489101
Change-Id: Ife76a6c02aaa2c3eefdb604343b849a213657900
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8058744
Commit-Queue: Stephen Chenney <schenney@chromium.org>
Reviewed-by: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1678269}
---

diff --git a/third_party/blink/renderer/core/paint/svg_object_painter.cc b/third_party/blink/renderer/core/paint/svg_object_painter.cc
index 35863f4..7b078882 100644
--- a/third_party/blink/renderer/core/paint/svg_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_object_painter.cc
@@ -31,6 +31,11 @@
     return false;
   }
 
+  if (paint_flags & PaintFlag::kPrivacyPreserving &&
+      !uri_resource->GetElement()->IsInCanvasSubtree()) {
+    return false;
+  }
+
   AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
       context_paint.object.StyleRef(), DarkModeFilter::ElementRole::kSVG));
   if (!uri_resource->ApplyShader(
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..da2ee27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG resources from outside the subtree</title>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+  <style>
+    a {
+      color: blue;
+    }
+    a:visited {
+      color: red;
+    }
+    canvas {
+      background-color: green;
+    }
+  </style>
+</head>
+<body>
+  <a href="">
+    <svg width="0" height="0">
+      <defs>
+        <linearGradient id="grad-outside">
+          <stop offset="0" stop-color="currentColor"/>
+          <stop offset="1" stop-color="currentColor"/>
+        </linearGradient>
+      </defs>
+    </svg>
+  </a>
+
+  <canvas id="canvas" width="100" height="100" layoutsubtree>
+    <div id="child">
+      <svg width="100" height="100">
+        <rect x="0" y="0" width="100" height="100" fill="url(#grad-outside)"/>
+      </svg>
+    </div>
+  </canvas>
+
+  <script>
+    window.onload = () => {
+      promise_test(async function(t) {
+        await waitForCanvasPaint(canvas);
+
+        var context = canvas.getContext("2d");
+
+        context.fillStyle = 'green';
+        context.fillRect(0, 0, 100, 100);
+
+        // Draw the element image
+        context.drawElementImage(child, 0, 0);
+
+        // The canvas should ignore the gradient and use transparent black.
+        let pixel = context.getImageData(50, 50, 1, 1).data;
+        assert_false(pixel[0] > 0, "The canvas should not use the gradient.");
+        assert_true(pixel[1] > 0, "The canvas should not use the gradient.");
+        assert_false(pixel[2] > 0, "The canvas should not use the gradient.");
+      });
+    };
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..71ab5f6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG patterns outside the subtree</title>
+  <link rel="help" href="https://github.com/WICG/html-in-canvas">
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+</head>
+<body>
+  <svg width="0" height="0">
+    <defs>
+      <pattern id="pattern" patternUnits="userSpaceOnUse" viewBox="0 0 100 100" width="100" height="100">
+        <image href="/images/red-100x100.png" x="0" y="0" width="100" height="100" preserveAspectRatio="none"/>
+      </pattern>
+    </defs>
+  </svg>
+
+  <canvas id="canvas" width="100" height="100" layoutsubtree>
+    <svg id=child width="100" height="100">
+      <rect x="0" y="0" width="100" height="100" fill="url(#pattern)"/>
+    </svg>
+  </canvas>
+
+  <script>
+    window.onload = () => {
+      promise_test(async function(t) {
+        const preloadImage = (url) => new Promise((resolve, reject) => {
+          const img = new Image();
+          img.onload = () => resolve(img);
+          img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
+          img.src = url;
+        });
+
+        const imageEl = document.querySelector('#pattern image');
+        await Promise.all([
+          preloadImage(imageEl.getAttribute('href')),
+        ]);
+
+        await waitForCanvasPaint(canvas);
+        var ctx = canvas.getContext('2d');
+        ctx.fillStyle = 'green';
+        ctx.fillRect(0, 0, 100, 100);
+        ctx.drawElementImage(child, 0, 0);
+
+        // The canvas should ignore the gradient and use transparent black.
+        let pixel = ctx.getImageData(50, 50, 1, 1).data;
+        assert_false(pixel[0] > 0, "The canvas should not use the pattern.");
+        assert_true(pixel[1] > 0, "The canvas should not use the pattern.");
+        assert_false(pixel[2] > 0, "The canvas should not use the pattern.");
+      });
+    }
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..6d540f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG resources from outside the subtree</title>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+  <style>
+    a {
+      color: blue;
+    }
+    a:visited {
+      color: red;
+    }
+    canvas {
+      background-color: green;
+    }
+  </style>
+</head>
+<body>
+  <a href="">
+    <svg width="0" height="0">
+      <defs>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..da2ee27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-linear-gradient-outside-subtree-ignored.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG resources from outside the subtree</title>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+  <style>
+    a {
+      color: blue;
+    }
+    a:visited {
+      color: red;
+    }
+    canvas {
+      background-color: green;
+    }
+  </style>
+</head>
+<body>
+  <a href="">
+    <svg width="0" height="0">
+      <defs>
+        <linearGradient id="grad-outside">
+          <stop offset="0" stop-color="currentColor"/>
+          <stop offset="1" stop-color="currentColor"/>
+        </linearGradient>
+      </defs>
+    </svg>
+  </a>
+
+  <canvas id="canvas" width="100" height="100" layoutsubtree>
+    <div id="child">
+      <svg width="100" height="100">
+        <rect x="0" y="0" width="100" height="100" fill="url(#grad-outside)"/>
+      </svg>
+    </div>
+  </canvas>
+
+  <script>
+    window.onload = () => {
+      promise_test(async function(t) {
+        await waitForCanvasPaint(canvas);
+
+        var context = canvas.getContext("2d");
+
+        context.fillStyle = 'green';
+        context.fillRect(0, 0, 100, 100);
+
+        // Draw the element image
+        context.drawElementImage(child, 0, 0);
+
+        // The canvas should ignore the gradient and use transparent black.
+        let pixel = context.getImageData(50, 50, 1, 1).data;
+        assert_false(pixel[0] > 0, "The canvas should not use the gradient.");
+        assert_true(pixel[1] > 0, "The canvas should not use the gradient.");
+        assert_false(pixel[2] > 0, "The canvas should not use the gradient.");
+      });
+    };
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..71ab5f6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-pattern-outside-subtree-ignored.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG patterns outside the subtree</title>
+  <link rel="help" href="https://github.com/WICG/html-in-canvas">
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+</head>
+<body>
+  <svg width="0" height="0">
+    <defs>
+      <pattern id="pattern" patternUnits="userSpaceOnUse" viewBox="0 0 100 100" width="100" height="100">
+        <image href="/images/red-100x100.png" x="0" y="0" width="100" height="100" preserveAspectRatio="none"/>
+      </pattern>
+    </defs>
+  </svg>
+
+  <canvas id="canvas" width="100" height="100" layoutsubtree>
+    <svg id=child width="100" height="100">
+      <rect x="0" y="0" width="100" height="100" fill="url(#pattern)"/>
+    </svg>
+  </canvas>
+
+  <script>
+    window.onload = () => {
+      promise_test(async function(t) {
+        const preloadImage = (url) => new Promise((resolve, reject) => {
+          const img = new Image();
+          img.onload = () => resolve(img);
+          img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
+          img.src = url;
+        });
+
+        const imageEl = document.querySelector('#pattern image');
+        await Promise.all([
+          preloadImage(imageEl.getAttribute('href')),
+        ]);
+
+        await waitForCanvasPaint(canvas);
+        var ctx = canvas.getContext('2d');
+        ctx.fillStyle = 'green';
+        ctx.fillRect(0, 0, 100, 100);
+        ctx.drawElementImage(child, 0, 0);
+
+        // The canvas should ignore the gradient and use transparent black.
+        let pixel = ctx.getImageData(50, 50, 1, 1).data;
+        assert_false(pixel[0] > 0, "The canvas should not use the pattern.");
+        assert_true(pixel[1] > 0, "The canvas should not use the pattern.");
+        assert_false(pixel[2] > 0, "The canvas should not use the pattern.");
+      });
+    }
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html
new file mode 100644
index 0000000..6d540f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/svg-radial-gradient-outside-subtree-ignored.tentative.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>drawElementImage does not use SVG resources from outside the subtree</title>
+  <script src='/resources/testharness.js'></script>
+  <script src='/resources/testharnessreport.js'></script>
+  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
+  <style>
+    a {
+      color: blue;
+    }
+    a:visited {
+      color: red;
+    }
+    canvas {
+      background-color: green;
+    }
+  </style>
+</head>
+<body>
+  <a href="">
+    <svg width="0" height="0">
+      <defs>
+        <radialGradient id="grad-outside">
+          <stop offset="0" stop-color="currentColor"/>
+          <stop offset="1" stop-color="currentColor"/>
+        </radialGradient>
+      </defs>
+    </svg>
+  </a>
+
+  <canvas id="canvas" width="100" height="100" layoutsubtree>
+    <div id="child">
+      <svg width="100" height="100">
+        <rect x="0" y="0" width="100" height="100" fill="url(#grad-outside)"/>
+      </svg>
+    </div>
+  </canvas>
+
+  <script>
+    window.onload = () => {
+      promise_test(async function(t) {
+        await waitForCanvasPaint(canvas);
+
+        var context = canvas.getContext("2d");
+
+        context.fillStyle = 'green';
+        context.fillRect(0, 0, 100, 100);
+
+        // Draw the element image
+        context.drawElementImage(child, 0, 0);
+
+        // The canvas should ignore the gradient and use transparent black.
+        let pixel = context.getImageData(50, 50, 1, 1).data;
+        assert_false(pixel[0] > 0, "The canvas should not use the gradient.");
+        assert_true(pixel[1] > 0, "The canvas should not use the gradient.");
+        assert_false(pixel[2] > 0, "The canvas should not use the gradient.");
+      });
+    };
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-color-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-color-ignored.tentative.html
deleted file mode 100644
index 5a149b2..0000000
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-color-ignored.tentative.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>drawElementImage does not reveal visited link colors</title>
-  <script src='/resources/testharness.js'></script>
-  <script src='/resources/testharnessreport.js'></script>
-  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
-  <style>
-    #link {
-      width: 100px;
-      height: 100px;
-      background-color: green;
-      color: green;
-      display: block;
-    }
-    #link:visited {
-      background-color: red; /* Visited color */
-      color: red; /* Visited color */
-    }
-  </style>
-</head>
-<body>
-  <canvas id="canvas" width="100" height="100" layoutsubtree>
-    <a id="link" href="">link</a>
-  </canvas>
-
-  <script>
-    window.onload = () => {
-      promise_test(async function(t) {
-        await waitForCanvasPaint(canvas);
-
-        var context = canvas.getContext("2d");
-
-        // Draw the element image
-        context.drawElementImage(link, 0, 0);
-
-        // The canvas should NOT reflect the visited state (red), it should paint the unvisited state (green).
-        let pixel = context.getImageData(10, 10, 1, 1).data;
-        assert_false(pixel[0] > 0, "The canvas should not reflect the visited state.");
-        assert_true(pixel[1] > 0, "The canvas should not reflect the visited state.");
-      });
-    };
-  </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-currentcolor-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-currentcolor-ignored.tentative.html
deleted file mode 100644
index d4d94d1..0000000
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/privacy/visited-link-currentcolor-ignored.tentative.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>drawElementImage does not reveal visited link colors through currentColor</title>
-  <script src='/resources/testharness.js'></script>
-  <script src='/resources/testharnessreport.js'></script>
-  <script src="/html/canvas/resources/wait-for-canvas-paint.js"></script>
-  <style>
-    #link {
-      color: green;
-    }
-    #link:visited {
-      color: red;
-    }
-    #target {
-      width: 100px;
-      height: 100px;
-      background-color: currentColor;
-    }
-  </style>
-</head>
-<body>
-  <a id="link" href="">
-    <canvas id="canvas" width="100" height="100" layoutsubtree>
-      <div id="target"></div>
-    </canvas>
-  </a>
-
-  <script>
-    window.onload = () => {
-      promise_test(async function(t) {
-        await waitForCanvasPaint(canvas);
-
-        var context = canvas.getContext("2d");
-
-        // Draw the element image
-        context.drawElementImage(target, 0, 0);
-
-        // The canvas should NOT reflect the visited state (red), it should paint the unvisited state (green).
-        let pixel = context.getImageData(10, 10, 1, 1).data;
-        assert_false(pixel[0] > 0, "The canvas should not reflect the visited state.");
-        assert_true(pixel[1] > 0, "The canvas should not reflect the visited state.");
-      });
-    };
-  </script>
-</body>
... (truncated)
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.