Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactPolicy bypass in CSS
DescriptionPolicy bypass in CSS
ComponentCSS
Bug ClassLogic Error
Tracker519603552
Fix commitf479b1fc9e0c (chromium/src) +42/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
TEST_F
third_party/blink/renderer/core/css/css_image_value_test.cc
modified

Files Changed

  • third_party/blink/renderer/core/css/css_image_value_test.cc
  • third_party/blink/renderer/core/css/css_url_data.h
From f479b1fc9e0cddd2a6d3c0b1c452dcf315411a44 Mon Sep 17 00:00:00 2001
From: Kevin Babbitt <kbabbitt@microsoft.com>
Date: Mon, 15 Jun 2026 09:37:19 -0700
Subject: [PATCH] Serialize relative URL for reference properties in dangling markup cases

Similar to https://crrev.com/c/7895140, this allows the potentially-
dangling-markup flag to be re-derived when the value is re-parsed.

Fixed: 519603552
Change-Id: I8318c523d8b72cb3b88c41fa90b091b6085ee115
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7929541
Commit-Queue: Kevin Babbitt <kbabbitt@microsoft.com>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1646881}
---

diff --git a/third_party/blink/renderer/core/css/css_image_value_test.cc b/third_party/blink/renderer/core/css/css_image_value_test.cc
index 3f12c03..47fb20b 100644
--- a/third_party/blink/renderer/core/css/css_image_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_image_value_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/web_script_source.h"
+#include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
@@ -201,4 +202,38 @@
   }
 }
 
+// Verifies that serializing a `filter: url(...)` computed value does not
+// strip the potentially_dangling_markup flag from the URL. The serializer
+// path (ComputedStyleUtils::ValueForFilter) builds a fresh CSSUrlData from
+// CSSUrlData::ValueForSerialization(), which must return the raw relative
+// URL when the flag is set so that re-parsing the serialized form does not
+// launder it.
+TEST_F(CSSImageValueTest, FilterUrlDoesNotLaunderDanglingMarkup) {
+  SimRequest main_resource("https://example.com/index.html", "text/html");
+
+  LoadURL("https://example.com/index.html");
+
+  main_resource.Complete(R"HTML(
+    <!doctype html>
+    <style>
+      #victim { filter: url('/exfil?\a <secret#x'); }
+    </style>
+    <div id="victim"></div>
+  )HTML");
+
+  test::RunPendingTasks();
+  GetDocument().UpdateStyleAndLayoutTree();
+  Compositor().BeginFrame();
+
+  auto* victim = GetDocument().getElementById(AtomicString("victim"));
+  ASSERT_TRUE(victim);
+  auto* computed = MakeGarbageCollected<CSSComputedStyleDeclaration>(victim);
+  String serialized = computed->GetPropertyValue(CSSPropertyID::kFilter);
+  // The serialized value must use the raw relative URL form rather than the
+  // canonicalized absolute URL with whitespace stripped. The latter would
+  // launder the dangling markup flag when re-parsed.
+  EXPECT_EQ(serialized.find("example.com"), kNotFound) << serialized;
+  EXPECT_NE(serialized.find("/exfil"), kNotFound) << serialized;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_url_data.h b/third_party/blink/renderer/core/css/css_url_data.h
index a3cdb86..a2872b7 100644
--- a/third_party/blink/renderer/core/css/css_url_data.h
+++ b/third_party/blink/renderer/core/css/css_url_data.h
@@ -102,8 +102,14 @@
   // Returns a copy where the referrer has been reset.
   const CSSUrlData* MakeWithoutReferrer() const;
 
+  // For dangling-markup URLs, return the raw relative form so that
+  // round-tripping through computed-style serialization cannot launder the
+  // potentially-dangling-markup flag. See MakeResolved()/MakeComputed() for
+  // the same rationale.
   const AtomicString& ValueForSerialization() const {
-    return is_local_ || absolute_url_.empty() ? relative_url_ : absolute_url_;
+    return is_local_ || absolute_url_.empty() || potentially_dangling_markup_
+               ? relative_url_
+               : absolute_url_;
   }
 
   const AtomicString& UnresolvedUrl() const { return relative_url_; }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/core/css/css_image_value_test.cc b/third_party/blink/renderer/core/css/css_image_value_test.cc
index 3f12c03..47fb20b 100644
--- a/third_party/blink/renderer/core/css/css_image_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_image_value_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/web_script_source.h"
+#include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
@@ -201,4 +202,38 @@
   }
 }
 
+// Verifies that serializing a `filter: url(...)` computed value does not
+// strip the potentially_dangling_markup flag from the URL. The serializer
+// path (ComputedStyleUtils::ValueForFilter) builds a fresh CSSUrlData from
+// CSSUrlData::ValueForSerialization(), which must return the raw relative
+// URL when the flag is set so that re-parsing the serialized form does not
+// launder it.
+TEST_F(CSSImageValueTest, FilterUrlDoesNotLaunderDanglingMarkup) {
+  SimRequest main_resource("https://example.com/index.html", "text/html");
+
+  LoadURL("https://example.com/index.html");
+
+  main_resource.Complete(R"HTML(
+    <!doctype html>
+    <style>
+      #victim { filter: url('/exfil?\a <secret#x'); }
+    </style>
+    <div id="victim"></div>
+  )HTML");
+
+  test::RunPendingTasks();
+  GetDocument().UpdateStyleAndLayoutTree();
+  Compositor().BeginFrame();
+
+  auto* victim = GetDocument().getElementById(AtomicString("victim"));
+  ASSERT_TRUE(victim);
+  auto* computed = MakeGarbageCollected<CSSComputedStyleDeclaration>(victim);
+  String serialized = computed->GetPropertyValue(CSSPropertyID::kFilter);
+  // The serialized value must use the raw relative URL form rather than the
+  // canonicalized absolute URL with whitespace stripped. The latter would
+  // launder the dangling markup flag when re-parsed.
+  EXPECT_EQ(serialized.find("example.com"), kNotFound) << serialized;
+  EXPECT_NE(serialized.find("/exfil"), kNotFound) << serialized;
+}
+
 }  // namespace blink
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.