Chrome · CSS
CVE-2026-17977
Logic Error in CSS
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fthird_party/blink/renderer/core/css/css_image_value_test.cc |
modified |
Files Changed
third_party/blink/renderer/core/css/css_image_value_test.ccthird_party/blink/renderer/core/css/css_url_data.h
Patch
From f479b1fc9e0cddd2a6d3c0b1c452dcf315411a44 Mon Sep 17 00:00:00 2001 From: Kevin Babbitt <[email protected]> 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 <[email protected]> Reviewed-by: Rune Lillesveen <[email protected]> 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.
References
On This Page