Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactPolicy bypass in Receiver
DescriptionPolicy bypass in Receiver
ComponentReceiver
Bug ClassLogic Error
Tracker513404032
Fix commit3bf03fd44bc6 (chromium/src) +56/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
for
components/url_rewrite/common/url_loader_throttle.cc
modified
if
components/url_rewrite/common/url_loader_throttle.cc
modified
TEST_F
components/url_rewrite/common/url_loader_throttle_unittest.cc
modified

Files Changed

  • components/url_rewrite/common/url_loader_throttle.cc
  • components/url_rewrite/common/url_loader_throttle_unittest.cc
From 3bf03fd44bc60a251de356c5010e2172d7d84923 Mon Sep 17 00:00:00 2001
From: Muyao Xu <[email protected]>
Date: Wed, 24 Jun 2026 12:04:10 -0700
Subject: [PATCH] Always Reevaluate access policies if a rule changes the URL

URLLoaderThrottle was evaluating embedder-defined access policies
(kAllow/kDeny) only once on the initial request URL. If rewrite rules
were applied sequentially, a rule could mutate the URL to a denied host
without triggering a policy re-check.

This CL updates WillStartRequest to re-evaluate the access policy on the
mutated request URL if a rule changes the URL.

Bug: 513404032
Change-Id: Ie251b72920383cc0e94334e17a2bc19af93f40e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7923228
Reviewed-by: David Song <[email protected]>
Commit-Queue: Muyao Xu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1651875}
---

diff --git a/components/url_rewrite/common/url_loader_throttle.cc b/components/url_rewrite/common/url_loader_throttle.cc
index 8579d1b..5eca3303 100644
--- a/components/url_rewrite/common/url_loader_throttle.cc
+++ b/components/url_rewrite/common/url_loader_throttle.cc
@@ -193,8 +193,18 @@
     return;
   }
 
-  for (const auto& rule : rules_->data->rules)
+  GURL current_url = request->url;
+  for (const auto& rule : rules_->data->rules) {
     ApplyRule(request, rule);
+    if (request->url != current_url) {
+      if (!IsRequestAllowed(request, rules_->data)) {
+        delegate_->CancelWithError(net::ERR_ABORTED,
+                                   "Resource load blocked by embedder policy.");
+        return;
+      }
+      current_url = request->url;
+    }
+  }
 
   if (!added_headers_.empty()) {
     original_origin_ = url::Origin::Create(request->url);
diff --git a/components/url_rewrite/common/url_loader_throttle_unittest.cc b/components/url_rewrite/common/url_loader_throttle_unittest.cc
index f4f062b..4646160e 100644
--- a/components/url_rewrite/common/url_loader_throttle_unittest.cc
+++ b/components/url_rewrite/common/url_loader_throttle_unittest.cc
@@ -408,4 +408,49 @@
   }
 }
 
+TEST_F(URLLoaderThrottleTest, ReplaceUrlReevaluatesPolicy) {
+  mojom::UrlRequestRewriteRulesPtr rules = mojom::UrlRequestRewriteRules::New();
+
+  {
+    mojom::UrlRequestRewriteReplaceUrlPtr replace_url =
+        mojom::UrlRequestRewriteReplaceUrl::New();
+    replace_url->url_ends_with = "/partner-endpoint";
+    replace_url->new_url = GURL("http://internal.host/api");
+
+    mojom::UrlRequestRulePtr rule = mojom::UrlRequestRule::New();
+    rule->hosts_filter =
+        std::optional<std::vector<std::string>>({"allowed.com"});
+    rule->actions.push_back(
+        mojom::UrlRequestAction::NewReplaceUrl(std::move(replace_url)));
+    rules->rules.push_back(std::move(rule));
+  }
+
+  {
+    mojom::UrlRequestRulePtr rule = mojom::UrlRequestRule::New();
+    rule->hosts_filter =
+        std::optional<std::vector<std::string>>({"internal.host"});
+    rule->actions.push_back(mojom::UrlRequestAction::NewPolicy(
+        mojom::UrlRequestAccessPolicy::kDeny));
+    rules->rules.push_back(std::move(rule));
+  }
+
+  TestThrottleDelegate delegate;
+  URLLoaderThrottle throttle(
+      base::MakeRefCounted<UrlRequestRewriteRules>(std::move(rules)),
+      CreateCorsExemptHeadersCallback({}));
+  throttle.set_delegate(&delegate);
+
+  bool unused_defer = false;
+
+  // Request to allowed.com/partner-endpoint should be rewritten to
+  // internal.host, which is denied, thus the request should be canceled.
+  network::ResourceRequest request;
+  request.url = GURL("http://allowed.com/partner-endpoint?q=ATTACKER");
+  throttle.WillStartRequest(&request, &unused_defer);
+
+  EXPECT_TRUE(delegate.canceled());
+  EXPECT_EQ(delegate.cancel_reason(),
+            "Resource load blocked by embedder policy.");
+}
+
 }  // namespace url_rewrite
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/url_rewrite/common/url_loader_throttle_unittest.cc b/components/url_rewrite/common/url_loader_throttle_unittest.cc
index f4f062b..4646160e 100644
--- a/components/url_rewrite/common/url_loader_throttle_unittest.cc
+++ b/components/url_rewrite/common/url_loader_throttle_unittest.cc
@@ -408,4 +408,49 @@
   }
 }
 
+TEST_F(URLLoaderThrottleTest, ReplaceUrlReevaluatesPolicy) {
+  mojom::UrlRequestRewriteRulesPtr rules = mojom::UrlRequestRewriteRules::New();
+
+  {
+    mojom::UrlRequestRewriteReplaceUrlPtr replace_url =
+        mojom::UrlRequestRewriteReplaceUrl::New();
+    replace_url->url_ends_with = "/partner-endpoint";
+    replace_url->new_url = GURL("http://internal.host/api");
+
+    mojom::UrlRequestRulePtr rule = mojom::UrlRequestRule::New();
+    rule->hosts_filter =
+        std::optional<std::vector<std::string>>({"allowed.com"});
+    rule->actions.push_back(
+        mojom::UrlRequestAction::NewReplaceUrl(std::move(replace_url)));
+    rules->rules.push_back(std::move(rule));
+  }
+
+  {
+    mojom::UrlRequestRulePtr rule = mojom::UrlRequestRule::New();
+    rule->hosts_filter =
+        std::optional<std::vector<std::string>>({"internal.host"});
+    rule->actions.push_back(mojom::UrlRequestAction::NewPolicy(
+        mojom::UrlRequestAccessPolicy::kDeny));
+    rules->rules.push_back(std::move(rule));
+  }
+
+  TestThrottleDelegate delegate;
+  URLLoaderThrottle throttle(
+      base::MakeRefCounted<UrlRequestRewriteRules>(std::move(rules)),
+      CreateCorsExemptHeadersCallback({}));
+  throttle.set_delegate(&delegate);
+
+  bool unused_defer = false;
+
+  // Request to allowed.com/partner-endpoint should be rewritten to
+  // internal.host, which is denied, thus the request should be canceled.
+  network::ResourceRequest request;
+  request.url = GURL("http://allowed.com/partner-endpoint?q=ATTACKER");
+  throttle.WillStartRequest(&request, &unused_defer);
+
+  EXPECT_TRUE(delegate.canceled());
+  EXPECT_EQ(delegate.cancel_reason(),
+            "Resource load blocked by embedder policy.");
+}
+
 }  // namespace url_rewrite
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.