Chrome · Extensions
CVE-2026-17976
Logic Error in Extensions
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TESTextensions/common/url_pattern_unittest.cc |
modified |
Files Changed
chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.ccextensions/common/url_pattern.ccextensions/common/url_pattern_unittest.cc
Patch
From b2d2df8dfd694f0b45ca00a6f493da35ca8831d6 Mon Sep 17 00:00:00 2001 From: Eva Su <[email protected]> Date: Fri, 12 Jun 2026 13:03:19 -0700 Subject: [PATCH] [Extensions] Fix ExtensionSettings bypass with multiple trailing dots The `CanonicalizeHostForMatching` function in `extensions/common/url_pattern.cc` previously only removed a single trailing dot from hostnames. This allowed a bypass of the `runtime_blocked_hosts` policy in ExtensionSettings if a URL contained multiple trailing dots. This change updates the function to remove all trailing dots from the hostname before matching against the policy. This prevents the bypass and correctly enforces the intended host restrictions. Fixed: 519455164 Change-Id: I5fbc7a891079e89293a15e008fe6a2c02ecf0e59 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7907778 Reviewed-by: Tim <[email protected]> Reviewed-by: Nina Satragno <[email protected]> Commit-Queue: Eva Su <[email protected]> Cr-Commit-Position: refs/heads/main@{#1646199} --- diff --git a/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc b/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc index 884857bb..0963a5c 100644 --- a/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc +++ b/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc @@ -200,8 +200,15 @@ {"http://localhost/", "localhost"}, // Sanity check empty domain parts. + // URLPattern trims all trailing dots from hosts for matching (both from the + // pattern and from the evaluated host). Thus, patterns or hosts with any + // number of trailing dots are canonicalized to the version without trailing + // dots and match. {"https://google.com./", "google.com"}, {"https://google.com./", "google.com."}, + {"https://google.com/", "google.com."}, + {"https://google.com/", "google.com.."}, + {"https://google.com../", "google.com"}, }; constexpr PatternRpIdPair kInvalidRelyingPartyTestCases[] = { @@ -240,12 +247,7 @@ {"https://not-google.com/", "google.com)"}, {"https://evil.appspot.com/", "appspot.com"}, {"https://evil.co.uk/", "co.uk"}, - // TODO(nsatragno): URLPattern erroneously trims trailing dots. Fix - // CanonicalizeHostForMatching and uncomment this line. - // {"https://google.com/", "google.com."}, - {"https://google.com/", "google.com.."}, {"https://google.com/", ".google.com"}, - {"https://google.com../", "google.com"}, {"https://.com/", "com."}, {"https://.co.uk/", "co.uk."}, {"https://1.2.3/", "1.2.3"}, diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc index c87bf37..1f733c42 100644 --- a/extensions/common/url_pattern.cc +++ b/extensions/common/url_pattern.cc @@ -123,12 +123,9 @@ return path; } -// Removes trailing dot from |host_piece| if any. +// Removes trailing dot(s) from |host_piece| if any. std::string_view CanonicalizeHostForMatching(std::string_view host_piece) { - if (base::EndsWith(host_piece, ".")) { - host_piece.remove_suffix(1); - } - return host_piece; + return base::TrimString(host_piece, ".", base::TRIM_TRAILING); } } // namespace diff --git a/extensions/common/url_pattern_unittest.cc b/extensions/common/url_pattern_unittest.cc index bb74cd70..dd9deee 100644 --- a/extensions/common/url_pattern_unittest.cc +++ b/extensions/common/url_pattern_unittest.cc @@ -967,6 +967,7 @@ TEST(ExtensionURLPatternTest, TrailingDotDomain) { const GURL normal_domain("http://example.com/"); const GURL trailing_dot_domain("http://example.com./"); + const GURL multiple_trailing_dots_domain("http://example.com../"); // Both patterns should match trailing dot and non trailing dot domains. More // information about this not obvious behaviour can be found in [1]. @@ -986,11 +987,20 @@ const URLPattern pattern(URLPattern::SCHEME_HTTP, "*://example.com/*"); EXPECT_TRUE(pattern.MatchesURL(normal_domain)); EXPECT_TRUE(pattern.MatchesURL(trailing_dot_domain)); + EXPECT_TRUE(pattern.MatchesURL(multiple_trailing_dots_domain)); const URLPattern trailing_pattern(URLPattern::SCHEME_HTTP, "*://example.com./*"); EXPECT_TRUE(trailing_pattern.MatchesURL(normal_domain)); EXPECT_TRUE(trailing_pattern.MatchesURL(trailing_dot_domain)); + EXPECT_TRUE(trailing_pattern.MatchesURL(multiple_trailing_dots_domain)); + + const URLPattern multiple_trailing_pattern(URLPattern::SCHEME_HTTP, + "*://example.com../*"); + EXPECT_TRUE(multiple_trailing_pattern.MatchesURL(normal_domain)); + EXPECT_TRUE(multiple_trailing_pattern.MatchesURL(trailing_dot_domain)); + EXPECT_TRUE( + multiple_trailing_pattern.MatchesURL(multiple_trailing_dots_domain)); } TEST(ExtensionURLPatternTest, MatchesEffectiveTLD) {
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc b/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc
index 884857bb..0963a5c 100644
--- a/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc
+++ b/chrome/browser/webauthn/chrome_web_authentication_delegate_unittest.cc
@@ -200,8 +200,15 @@
{"http://localhost/", "localhost"},
// Sanity check empty domain parts.
+ // URLPattern trims all trailing dots from hosts for matching (both from the
+ // pattern and from the evaluated host). Thus, patterns or hosts with any
+ // number of trailing dots are canonicalized to the version without trailing
+ // dots and match.
{"https://google.com./", "google.com"},
{"https://google.com./", "google.com."},
+ {"https://google.com/", "google.com."},
+ {"https://google.com/", "google.com.."},
+ {"https://google.com../", "google.com"},
};
constexpr PatternRpIdPair kInvalidRelyingPartyTestCases[] = {
@@ -240,12 +247,7 @@
{"https://not-google.com/", "google.com)"},
{"https://evil.appspot.com/", "appspot.com"},
{"https://evil.co.uk/", "co.uk"},
- // TODO(nsatragno): URLPattern erroneously trims trailing dots. Fix
- // CanonicalizeHostForMatching and uncomment this line.
- // {"https://google.com/", "google.com."},
- {"https://google.com/", "google.com.."},
{"https://google.com/", ".google.com"},
- {"https://google.com../", "google.com"},
{"https://.com/", "com."},
{"https://.co.uk/", "co.uk."},
{"https://1.2.3/", "1.2.3"},
diff --git a/extensions/common/url_pattern_unittest.cc b/extensions/common/url_pattern_unittest.cc
index bb74cd70..dd9deee 100644
--- a/extensions/common/url_pattern_unittest.cc
+++ b/extensions/common/url_pattern_unittest.cc
@@ -967,6 +967,7 @@
TEST(ExtensionURLPatternTest, TrailingDotDomain) {
const GURL normal_domain("http://example.com/");
const GURL trailing_dot_domain("http://example.com./");
+ const GURL multiple_trailing_dots_domain("http://example.com../");
// Both patterns should match trailing dot and non trailing dot domains. More
// information about this not obvious behaviour can be found in [1].
@@ -986,11 +987,20 @@
const URLPattern pattern(URLPattern::SCHEME_HTTP, "*://example.com/*");
EXPECT_TRUE(pattern.MatchesURL(normal_domain));
EXPECT_TRUE(pattern.MatchesURL(trailing_dot_domain));
+ EXPECT_TRUE(pattern.MatchesURL(multiple_trailing_dots_domain));
const URLPattern trailing_pattern(URLPattern::SCHEME_HTTP,
"*://example.com./*");
EXPECT_TRUE(trailing_pattern.MatchesURL(normal_domain));
EXPECT_TRUE(trailing_pattern.MatchesURL(trailing_dot_domain));
+ EXPECT_TRUE(trailing_pattern.MatchesURL(multiple_trailing_dots_domain));
+
+ const URLPattern multiple_trailing_pattern(URLPattern::SCHEME_HTTP,
+ "*://example.com../*");
+ EXPECT_TRUE(multiple_trailing_pattern.MatchesURL(normal_domain));
+ EXPECT_TRUE(multiple_trailing_pattern.MatchesURL(trailing_dot_domain));
+ EXPECT_TRUE(
+ multiple_trailing_pattern.MatchesURL(multiple_trailing_dots_domain));
}
TEST(ExtensionURLPatternTest, MatchesEffectiveTLD) {
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