Chrome · Enterprise
CVE-2026-17923
Logic Error in Enterprise
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/url_matcher/url_matcher.cc |
modified |
Files Changed
components/url_matcher/url_matcher.cccomponents/url_matcher/url_matcher_unittest.cc
Patch
From 4a017a0d4374a27fa572b65df2be471bda4421a0 Mon Sep 17 00:00:00 2001
From: Dominic Battre <battre@chromium.org>
Date: Wed, 03 Jun 2026 00:38:00 -0700
Subject: [PATCH] Collapse trailing dots in URLMatcher host suffix canonicalization
This change modifies
`URLMatcherConditionFactory::CanonicalizeHostSuffix` to strip all
trailing dots from the suffix and append exactly one dot.
This collapses suffixes like "host", "host.", and "host.." to the same
canonical "host.". This prevents FQDNs with multiple trailing dots from
bypassing host-suffix filters, as GURL accepts hosts with empty labels
(e.g., trailing dots) and can present them with multiple dots, whereas
the filter side might only store one.
Also added unit tests in `url_matcher_unittest.cc` to verify the fix
with various combinations of trailing dots on both the URL host and the
matching patterns.
Fixed: 513612928
Change-Id: I4ef10d3b1a85fc016458ba1aac417af16a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7894782
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
Auto-Submit: Dominic Battré <battre@chromium.org>
Commit-Queue: Steinar H Gunderson <sesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1640754}
---
diff --git a/components/url_matcher/url_matcher.cc b/components/url_matcher/url_matcher.cc
index bb45ca1..20b33f3 100644
--- a/components/url_matcher/url_matcher.cc
+++ b/components/url_matcher/url_matcher.cc
@@ -526,10 +526,17 @@
std::string URLMatcherConditionFactory::CanonicalizeHostSuffix(
const std::string& suffix) const {
- if (suffix.empty()) {
+ // Strip all trailing dots, then append exactly one. This collapses
+ // "host", "host." and "host.." (etc.) to the same canonical "host." so
+ // that multi-dot FQDN forms cannot bypass host-suffix filters. GURL
+ // accepts hosts with empty labels (see url/url_idna_icu.cc), so the
+ // URL side can otherwise present "host.." while the filter side stores
+ // "host.".
+ const size_t end = suffix.find_last_not_of('.');
+ if (end == std::string::npos) {
return ".";
}
- return suffix.back() == '.' ? suffix : suffix + ".";
+ return suffix.substr(0, end + 1) + ".";
}
std::string URLMatcherConditionFactory::CanonicalizeHostPrefix(
diff --git a/components/url_matcher/url_matcher_unittest.cc b/components/url_matcher/url_matcher_unittest.cc
index 42020eb..1a84895d 100644
--- a/components/url_matcher/url_matcher_unittest.cc
+++ b/components/url_matcher/url_matcher_unittest.cc
@@ -320,6 +320,11 @@
"&ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab"
"&q=chrome%20is%20awesome");
std::string url2 = factory.CanonicalizeURLForComponentSearches(gurl2);
+ GURL gurl3(
+ "https://www.google.com..:1234/webhp?sourceid=chrome-instant"
+ "&ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab"
+ "&q=chrome%20is%20awesome");
+ std::string url3 = factory.CanonicalizeURLForComponentSearches(gurl3);
// Test host component.
EXPECT_TRUE(Matches(factory.CreateHostPrefixCondition(std::string()), url));
@@ -335,34 +340,81 @@
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url2));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url3));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url2));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url3));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(".com"), url));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(".com"), url3));
EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition("www.google.com"), url));
EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com"), url3));
+ EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com"), url));
EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com"), url2));
EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com"), url3));
+ EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com."), url3));
+
+ // Suffix patterns with multiple trailing dots should be canonicalized to a
+ // single trailing dot and match.
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com..."), url));
+
EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("www"), url));
EXPECT_FALSE(
Matches(factory.CreateHostSuffixCondition("www.google.com/"), url));
EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("webhp"), url));
EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition(std::string()), url));
+ EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition(std::string()), url3));
EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition("www"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition("www.google.com"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition("www.google.com"), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com"), url3));
EXPECT_FALSE(
Matches(factory.CreateHostEqualsCondition("www.google.com/"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition(".www.google.com."), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition(".www.google.com."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com."), url3));
+
+ // Equals patterns with multiple trailing dots should be canonicalized to a
+ // single trailing dot and match.
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url3));
// Test path component.
EXPECT_TRUE(Matches(factory.CreatePathPrefixCondition(std::string()), url));
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/components/url_matcher/url_matcher_unittest.cc b/components/url_matcher/url_matcher_unittest.cc
index 42020eb..1a84895d 100644
--- a/components/url_matcher/url_matcher_unittest.cc
+++ b/components/url_matcher/url_matcher_unittest.cc
@@ -320,6 +320,11 @@
"&ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab"
"&q=chrome%20is%20awesome");
std::string url2 = factory.CanonicalizeURLForComponentSearches(gurl2);
+ GURL gurl3(
+ "https://www.google.com..:1234/webhp?sourceid=chrome-instant"
+ "&ie=UTF-8&ion=1#hl=en&output=search&sclient=psy-ab"
+ "&q=chrome%20is%20awesome");
+ std::string url3 = factory.CanonicalizeURLForComponentSearches(gurl3);
// Test host component.
EXPECT_TRUE(Matches(factory.CreateHostPrefixCondition(std::string()), url));
@@ -335,34 +340,81 @@
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url2));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(std::string()), url3));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url2));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition("com"), url3));
EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(".com"), url));
+ EXPECT_TRUE(Matches(factory.CreateHostSuffixCondition(".com"), url3));
EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition("www.google.com"), url));
EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com"), url3));
+ EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com"), url));
EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com"), url2));
EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com"), url3));
+ EXPECT_TRUE(
Matches(factory.CreateHostSuffixCondition(".www.google.com."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com."), url3));
+
+ // Suffix patterns with multiple trailing dots should be canonicalized to a
+ // single trailing dot and match.
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition("www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostSuffixCondition(".www.google.com..."), url));
+
EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("www"), url));
EXPECT_FALSE(
Matches(factory.CreateHostSuffixCondition("www.google.com/"), url));
EXPECT_FALSE(Matches(factory.CreateHostSuffixCondition("webhp"), url));
EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition(std::string()), url));
+ EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition(std::string()), url3));
EXPECT_FALSE(Matches(factory.CreateHostEqualsCondition("www"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition("www.google.com"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition("www.google.com"), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com"), url3));
EXPECT_FALSE(
Matches(factory.CreateHostEqualsCondition("www.google.com/"), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition(".www.google.com."), url));
EXPECT_TRUE(
Matches(factory.CreateHostEqualsCondition(".www.google.com."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com."), url3));
+
+ // Equals patterns with multiple trailing dots should be canonicalized to a
+ // single trailing dot and match.
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition("www.google.com.."), url3));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url2));
+ EXPECT_TRUE(
+ Matches(factory.CreateHostEqualsCondition(".www.google.com.."), url3));
// Test path component.
EXPECT_TRUE(Matches(factory.CreatePathPrefixCondition(std::string()), url));
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