Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactPolicy bypass in Content Settings
DescriptionPolicy bypass in Content Settings
ComponentContent Settings
Bug ClassLogic Error
Tracker498373018
Fix commit84a5f8efa4c5 (chromium/src) +24/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
components/content_settings/core/common/host_indexed_content_settings.cc
modified
TEST_F
components/content_settings/core/common/host_indexed_content_settings_unittest.cc
modified

Files Changed

  • components/content_settings/core/common/host_indexed_content_settings.cc
  • components/content_settings/core/common/host_indexed_content_settings_unittest.cc
From 84a5f8efa4c53c1b884d65929a57c22cd270cdc0 Mon Sep 17 00:00:00 2001
From: Christian Dullweber <dullweber@chromium.org>
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 <dullweber@chromium.org>
Reviewed-by: Martin Šrámek <msramek@chromium.org>
Commit-Queue: Christian Dullweber <dullweber@chromium.org>
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
Loading diff…

Regression Test / PoC

shipped with the fix
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
Loading diff…

Original Bug Report

reported by vm...@google.com

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)

  1. Configure a content setting rule: primaryPattern: '*', secondaryPattern: '[*.]example.com', setting: BLOCK (e.g., block third-party cookies for this site and its subdomains).
  2. Navigate the browser to a subdomain of the target: https://www.example.com/.
  3. From the page context, initiate a subresource fetch to an IP literal: fetch('http://1.2.3.4/set-cookie', {credentials: 'include'}).
  4. The settings evaluator checks Find(primary_url="http://1.2.3.4/", secondary_url="https://www.example.com/").
  5. The IP check passes, the subdomain walk for www.example.com is 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.

View on issue tracker