CVE-2026-11252
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcomponents/content_settings/core/common/host_indexed_content_settings.cc |
modified | |
TEST_Fcomponents/content_settings/core/common/host_indexed_content_settings_unittest.cc |
modified |
Files Changed
components/content_settings/core/common/host_indexed_content_settings.cccomponents/content_settings/core/common/host_indexed_content_settings_unittest.cc
Patch
From 84a5f8efa4c53c1b884d65929a57c22cd270cdc0 Mon Sep 17 00:00:00 2001 From: Christian Dullweber <[email protected]> Date: Thu, 09 Apr 2026 08:17:52 -0700 Subject: [PATCH] ContentSettings: Fix index lookup for secondary URL The indexed lookup for content settings is not handling the case correctly where we request an IP address from a regular hostname. Bug: 498373018 Change-Id: Ide4b08954dcf4f09f1504e526ba0136d3739a72e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7734965 Auto-Submit: Christian Dullweber <[email protected]> Reviewed-by: Martin Šrámek <[email protected]> Commit-Queue: Christian Dullweber <[email protected]> Cr-Commit-Position: refs/heads/main@{#1612248} --- diff --git a/components/content_settings/core/common/host_indexed_content_settings.cc b/components/content_settings/core/common/host_indexed_content_settings.cc index 1f286ca..feff992 100644 --- a/components/content_settings/core/common/host_indexed_content_settings.cc +++ b/components/content_settings/core/common/host_indexed_content_settings.cc @@ -75,14 +75,18 @@ return it == settings.end() ? nullptr : &*it; } +// Looks up an entry in |indexed_content_setting|. |index_key| must be the URL that +// was used to populate this index structure. const RuleEntry* FindInHostToContentSettings( const GURL& primary_url, const GURL& secondary_url, const HostIndexedContentSettings::HostToContentSettings& indexed_content_setting, - std::string_view host, + const GURL& index_key, const base::Clock* clock, bool return_expired_settings) { + std::string_view host = index_key.host(); + if (host.empty() || indexed_content_setting.empty()) { return nullptr; } @@ -91,7 +95,7 @@ if (host.back() == '.') { host.remove_suffix(1); } - if (primary_url.HostIsIPAddress()) { + if (index_key.HostIsIPAddress()) { auto it = indexed_content_setting.find(host); if (it != indexed_content_setting.end()) { auto* result = FindContentSetting(primary_url, secondary_url, it->second, @@ -297,14 +301,14 @@ const GURL& primary_url, const GURL& secondary_url) const { const RuleEntry* found = FindInHostToContentSettings( - primary_url, secondary_url, primary_host_indexed_, primary_url.host(), - clock_, return_expired_settings_); + primary_url, secondary_url, primary_host_indexed_, + /*index_key=*/primary_url, clock_, return_expired_settings_); if (found) { return found; } found = FindInHostToContentSettings( - primary_url, secondary_url, secondary_host_indexed_, secondary_url.host(), - clock_, return_expired_settings_); + primary_url, secondary_url, secondary_host_indexed_, + /*index_key=*/secondary_url, clock_, return_expired_settings_); if (found) { return found; } diff --git a/components/content_settings/core/common/host_indexed_content_settings_unittest.cc b/components/content_settings/core/common/host_indexed_content_settings_unittest.cc index 7ddfd8f..f05540f8 100644 --- a/components/content_settings/core/common/host_indexed_content_settings_unittest.cc +++ b/components/content_settings/core/common/host_indexed_content_settings_unittest.cc @@ -353,5 +353,19 @@ EXPECT_EQ(ToVector(indices[2]), expected_2); } +TEST_F(HostIndexedContentSettingsTest, CheckSubresourceIsIP) { + GURL test_primary_url("http://192.168.1.2/"); + GURL test_secondary_url("https://www.example.com"); + ContentSettingsForOneType test_settings = { + CreateSetting("*", "[*.]example.com", CONTENT_SETTING_BLOCK), + }; + HostIndexedContentSettings index = FromVector(test_settings); + + auto* result = index.Find(test_primary_url, test_secondary_url); + ASSERT_TRUE(result); + EXPECT_EQ(ValueToContentSetting(result->second.value), CONTENT_SETTING_BLOCK); + EXPECT_THAT(ToVector(index), testing::ContainerEq(test_settings)); +} + } // namespace } // namespace content_settings
Regression Test / PoC
diff --git a/components/content_settings/core/common/host_indexed_content_settings_unittest.cc b/components/content_settings/core/common/host_indexed_content_settings_unittest.cc
index 7ddfd8f..f05540f8 100644
--- a/components/content_settings/core/common/host_indexed_content_settings_unittest.cc
+++ b/components/content_settings/core/common/host_indexed_content_settings_unittest.cc
@@ -353,5 +353,19 @@
EXPECT_EQ(ToVector(indices[2]), expected_2);
}
+TEST_F(HostIndexedContentSettingsTest, CheckSubresourceIsIP) {
+ GURL test_primary_url("http://192.168.1.2/");
+ GURL test_secondary_url("https://www.example.com");
+ ContentSettingsForOneType test_settings = {
+ CreateSetting("*", "[*.]example.com", CONTENT_SETTING_BLOCK),
+ };
+ HostIndexedContentSettings index = FromVector(test_settings);
+
+ auto* result = index.Find(test_primary_url, test_secondary_url);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(ValueToContentSetting(result->second.value), CONTENT_SETTING_BLOCK);
+ EXPECT_THAT(ToVector(index), testing::ContainerEq(test_settings));
+}
+
} // namespace
} // namespace content_settings
Original Bug Report
ContentSettings policy bypass via IP address subresources
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A logic flaw in HostIndexedContentSettings incorrectly skips subdomain-matching for secondary URLs when the primary URL is an IP address. This bypasses domain-wildcard rules, allowing unauthorized cookie access or content setting circumvention.
Affected files:
components/content_settings/core/common/host_indexed_content_settings.cc
Estimated timestamp from git blame: 2024-01-31
Suggested Fix
Update FindInHostToContentSettings in components/content_settings/core/common/host_indexed_content_settings.cc to evaluate whether the host parameter (the string currently being queried) is an IP address, rather than hardcoding the condition to primary_url.HostIsIPAddress(). Passing a boolean flag from the caller (e.g., is_ip_address) based on the specific URL being queried (primary vs. secondary) will resolve the incorrect branching.
Context & Root Cause The general content settings indexing mechanism and map selection are validated. The vulnerability lies specifically in the subdomain walk logic during secondary URL lookups.
When HostIndexedContentSettings::Find queries the secondary_host_indexed_ map, it passes secondary_url.host() as the host parameter to FindInHostToContentSettings. However, line 94 incorrectly branches based on primary_url.HostIsIPAddress():
// components/content_settings/core/common/host_indexed_content_settings.cc
if (primary_url.HostIsIPAddress()) {
// Performs exact match only
auto it = indexed_content_setting.find(host);
...
} else {
// Performs subdomain walk
std::string_view subdomain(host);
while (!subdomain.empty()) {
...
If the primary URL is an IP address (e.g., http://1.2.3.4/), the true branch executes. This forces an exact-match lookup for the secondary host (e.g., www.example.com). The required subdomain stripping loop (while (!subdomain.empty())) is completely skipped. Consequently, secondary-indexed domain-wildcard policies (e.g., [*.]example.com) are bypassed because the required lookup for example.com never occurs.
Potential Reproduction Steps (Note: These are suggested steps; our tooling agent cannot yet execute local code to provide a working PoC)
- Configure a content setting rule:
primaryPattern: '*',secondaryPattern: '[*.]example.com',setting: BLOCK(e.g., block third-party cookies for this site and its subdomains). - Navigate the browser to a subdomain of the target:
https://www.example.com/. - From the page context, initiate a subresource fetch to an IP literal:
fetch('http://1.2.3.4/set-cookie', {credentials: 'include'}). - The settings evaluator checks
Find(primary_url="http://1.2.3.4/", secondary_url="https://www.example.com/"). - The IP check passes, the subdomain walk for
www.example.comis skipped, and the cookie is erroneously allowed.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.