Low chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in DevTools
DescriptionMissing authorization in DevTools
ComponentDevTools
Bug ClassAuthorization Error
Tracker514456975
Fix commit78293d960829 (chromium/src) +148/-9
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
IN_PROC_BROWSER_TEST_F
content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
modified
BindRepeating
content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
modified

Files Changed

  • third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
  • third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
  • content/browser/devtools/protocol/devtools_network_resource_loader.h
  • content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
  • content/browser/devtools/protocol/network_handler.cc
  • content/browser/devtools/protocol/devtools_network_resource_loader.cc
From 78293d960829a772cb29d539bee35edeb23d5edc Mon Sep 17 00:00:00 2001
From: Samiya Caur <samiyac@chromium.org>
Date: Tue, 08 Sep 2026 04:54:03 -0700
Subject: [PATCH] Enforce CSP on redirects in loadNetworkResource

Fixed: 514456975
Change-Id: I5c880abb89bedd2d3190de7afab42482dd242a6d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8322187
Reviewed-by: Alex Rudenko <alexrudenko@chromium.org>
Commit-Queue: Samiya Caur <samiyac@chromium.org>
Reviewed-by: Maksim Sadym <sadym@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1693703}
---
diff --git a/content/browser/devtools/protocol/devtools_network_resource_loader.cc b/content/browser/devtools/protocol/devtools_network_resource_loader.cc
index 73e6aa1..6cca7077 100644
--- a/content/browser/devtools/protocol/devtools_network_resource_loader.cc
+++ b/content/browser/devtools/protocol/devtools_network_resource_loader.cc
@@ -11,7 +11,9 @@
 #include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
 #include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 
@@ -22,11 +24,13 @@
     network::ResourceRequest resource_request,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
-    CompletionCallback completion_callback)
+    CompletionCallback completion_callback,
+    RedirectCheckCallback redirect_check_callback)
     : resource_request_(std::move(resource_request)),
       traffic_annotation_(traffic_annotation),
       url_loader_factory_(std::move(url_loader_factory)),
-      completion_callback_(std::move(completion_callback)) {
+      completion_callback_(std::move(completion_callback)),
+      redirect_check_callback_(std::move(redirect_check_callback)) {
   DownloadAsStream();
 }
 
@@ -47,7 +51,8 @@
     Caching caching,
     Credentials include_credentials,
     CompletionCallback completion_callback,
-    bool is_outermost_main_frame) {
+    bool is_outermost_main_frame,
+    RedirectCheckCallback redirect_check_callback) {
   network::ResourceRequest resource_request;
   resource_request.url = std::move(gurl);
   resource_request.request_initiator = origin;
@@ -90,7 +95,8 @@
 
   return base::WrapUnique(new DevToolsNetworkResourceLoader(
       std::move(resource_request), traffic_annotation,
-      std::move(url_loader_factory), std::move(completion_callback)));
+      std::move(url_loader_factory), std::move(completion_callback),
+      std::move(redirect_check_callback)));
 }
 
 void DevToolsNetworkResourceLoader::OnRetry(base::OnceClosure start_retry) {
@@ -102,9 +108,27 @@
   loader_ = network::SimpleURLLoader::Create(
       std::make_unique<network::ResourceRequest>(resource_request_),
       traffic_annotation_);
+  loader_->SetOnRedirectCallback(base::BindRepeating(
+      &DevToolsNetworkResourceLoader::OnRedirect, base::Unretained(this)));
   loader_->DownloadAsStream(url_loader_factory_.get(), this);
 }
 
+void DevToolsNetworkResourceLoader::OnRedirect(
+    const GURL& /*url_before_redirect*/,
+    const net::RedirectInfo& redirect_info,
+    const network::mojom::URLResponseHead& response_head,
+    std::vector<std::string>* /*removed_headers*/) {
+  if (redirect_check_callback_ &&
+      !redirect_check_callback_.Run(redirect_info)) {
+    const net::HttpResponseHeaders* response_headers =
+        response_head.headers.get();
+    loader_.reset();
+    std::move(completion_callback_)
+        .Run(this, response_headers, /*success=*/false, net::ERR_BLOCKED_BY_CSP,
+             "");
+  }
+}
+
 void DevToolsNetworkResourceLoader::OnDataReceived(std::string_view chunk,
                                                    base::OnceClosure resume) {
   content_.append(chunk);
diff --git a/content/browser/devtools/protocol/devtools_network_resource_loader.h b/content/browser/devtools/protocol/devtools_network_resource_loader.h
index 47d2588..58b3220ef 100644
--- a/content/browser/devtools/protocol/devtools_network_resource_loader.h
+++ b/content/browser/devtools/protocol/devtools_network_resource_loader.h
@@ -8,8 +8,10 @@
 #include <memory>
 #include <string_view>
 
+#include "base/functional/callback.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
@@ -33,6 +35,8 @@
                               bool success,
                               int net_error,
                               std::string content)>;
+  using RedirectCheckCallback =
+      base::RepeatingCallback<bool(const net::RedirectInfo& redirect_info)>;
   enum class Caching { kBypass, kDefault };
   enum class Credentials { kInclude, kSameSite };
 
@@ -48,7 +52,8 @@
       Caching caching,
       Credentials include_credentials,
       CompletionCallback complete_callback,
-      bool is_outermost_main_frame = false);
+      bool is_outermost_main_frame = false,
+      RedirectCheckCallback redirect_check_callback = {});
 
   ~DevToolsNetworkResourceLoader() override;
 
@@ -62,7 +67,8 @@
       network::ResourceRequest resource_request,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory,
-      CompletionCallback complete_callback);
+      CompletionCallback complete_callback,
+      RedirectCheckCallback redirect_check_callback);
   void DownloadAsStream();
 
   void OnDataReceived(std::string_view chunk,
@@ -72,11 +78,17 @@
 
   void OnRetry(base::OnceClosure start_retry) override;
 
+  void OnRedirect(const GURL& url_before_redirect,
+                  const net::RedirectInfo& redirect_info,
+                  const network::mojom::URLResponseHead& response_head,
+                  std::vector<std::string>* removed_headers);
+
   const network::ResourceRequest resource_request_;
   const net::NetworkTrafficAnnotationTag traffic_annotation_;
   std::unique_ptr<network::SimpleURLLoader> loader_;
   mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_;
   CompletionCallback completion_callback_;
+  RedirectCheckCallback redirect_check_callback_;
   std::string content_;
 };
 
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 9afa1f2b..73dcf73 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -4197,6 +4197,12 @@
       return;
     }
 
+    if (!frame->policy_container_host()) {
+      callback->sendFailure(
+          Response::ServerError("Frame does not have a policy container"));
+      return;
+    }
+
     RenderFrameHostCSPContext csp_context(frame);
 
     network::CSPCheckResult result = csp_context.IsAllowedByCsp(
@@ -4230,12 +4236,34 @@
       return;
     }
 
+    auto redirect_check = base::BindRepeating(
+        [](scoped_refptr<PolicyContainerHost> policy_container_host,
+           base::WeakPtr<RenderFrameHostImpl> frame, const GURL& initial_url,
+           const net::RedirectInfo& redirect_info) -> bool {
+          if (!frame || !policy_container_host ||
+              frame->policy_container_host() != policy_container_host.get()) {
+            return false;
+          }
+          RenderFrameHostCSPContext csp_context(frame.get());
+          network::CSPCheckResult result = csp_context.IsAllowedByCsp(
+              policy_container_host->policies().content_security_policies,
+              network::mojom::CSPDirectiveName::ConnectSrc,
+              redirect_info.new_url, initial_url,
+              /*has_followed_redirect=*/true,
+              /*source_location=*/nullptr,
+              network::CSPContext::CHECK_ENFORCED_CSP,
+              /*is_opaque_fenced_frame=*/false);
+          return result.IsAllowed();
+        },
+        base::WrapRefCounted(frame->policy_container_host()),
+        frame->GetWeakPtr(), gurl);
+
     url_loader_factory.Bind(std::move(factory));
     auto loader = DevToolsNetworkResourceLoader::Create(
         std::move(url_loader_factory), std::move(gurl),
         frame->GetLastCommittedOrigin(), frame->ComputeSiteForCookies(),
         caching, include_credentials, std::move(complete_callback),
-        frame->IsOutermostMainFrame());
+        frame->IsOutermostMainFrame(), std::move(redirect_check));
     loaders_.emplace(std::move(loader), std::move(callback));
     return;
   }
diff --git a/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc b/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
index a226fce5..bf5c9d90 100644
--- a/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
@@ -136,13 +136,16 @@
   std::unique_ptr<protocol::DevToolsNetworkResourceLoader> CreateLoader(
       GURL url,
       protocol::DevToolsNetworkResourceLoader::Caching caching,
-      protocol::DevToolsNetworkResourceLoader::CompletionCallback callback) {
+      protocol::DevToolsNetworkResourceLoader::CompletionCallback callback,
+      protocol::DevToolsNetworkResourceLoader::RedirectCheckCallback
+          redirect_check_callback = {}) {
     return protocol::DevToolsNetworkResourceLoader::Create(
         CreateURLLoaderFactory(), std::move(url),
         current_frame_host()->GetLastCommittedOrigin(),
         current_frame_host()->ComputeSiteForCookies(), caching,
         protocol::DevToolsNetworkResourceLoader::Credentials::kInclude,
-        std::move(callback), current_frame_host()->IsOutermostMainFrame());
+        std::move(callback), current_frame_host()->IsOutermostMainFrame(),
+        std::move(redirect_check_callback));
   }
 
  protected:
@@ -302,4 +305,47 @@
   EXPECT_FALSE(request->load_flags & net::LOAD_BYPASS_CACHE);
 }
 
+IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest, RedirectAllowed) {
+  base::RunLoop run_loop;
+  const GURL source_map_url(
+      embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
+  const GURL redirect_url(embedded_test_server()->GetURL(
+      "a.com", "/server-redirect?" + source_map_url.spec()));
+
+  auto loader = CreateLoader(
+      redirect_url, protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
+      base::BindOnce(CheckSuccess, this, &run_loop),
+      base::BindRepeating([](const net::RedirectInfo&) { return true; }));
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
+                       RedirectBlockedByCallback) {
+  base::RunLoop run_loop;
+  const GURL source_map_url(
+      embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
+  const GURL redirect_url(embedded_test_server()->GetURL(
+      "a.com", "/server-redirect?" + source_map_url.spec()));
+
+  auto complete_callback = base::BindOnce(
+      [](base::RunLoop* run_loop,
+         protocol::DevToolsNetworkResourceLoader* loader,
+         const net::HttpResponseHeaders* rh, bool success, int net_error,
+         std::string content) {
+        EXPECT_FALSE(success);
+        EXPECT_EQ(net_error, net::ERR_BLOCKED_BY_CSP);
+        EXPECT_EQ(content, "");
+        EXPECT_TRUE(rh);
+        EXPECT_EQ(rh->response_code(), 301);
+        run_loop->Quit();
+      },
+      &run_loop);
+
+  auto loader = CreateLoader(
+      redirect_url, protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
+      std::move(complete_callback),
+      base::BindRepeating([](const net::RedirectInfo&) { return false; }));
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
index 7c31915..4508dc0a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
@@ -11,4 +11,21 @@
         success : true
     }
 }
+Response for fetch with redirect to same-origin resource (should be allowed by CSP): {
+    resource : {
+        headers : <object>
+        httpStatusCode : 200
+        stream : 2
+        success : true
+    }
+}
+Response for fetch with redirect to cross-origin resource (should be blocked by CSP): {
+    resource : {
+        headers : <object>
+        httpStatusCode : 302
+        netError : -30
+        netErrorName : net::ERR_BLOCKED_BY_CSP
+        success : false
+    }
+}
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
index 1fc7c8e0..f91479db 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
@@ -27,5 +27,17 @@
       sameOriginUrl,
       `Response for fetch with same-origin resource (should be allowed by CSP):`);
 
+  const redirectSameOriginUrl =
+      `http://127.0.0.1:8000/resources/redirect.php?url=http://127.0.0.1:8000/inspector-protocol/network/resources/source.map`;
+  await loadResource(
+      redirectSameOriginUrl,
+      `Response for fetch with redirect to same-origin resource (should be allowed by CSP):`);
+
+  const redirectCrossOriginUrl =
+      `http://127.0.0.1:8000/resources/redirect.php?url=https://localhost:8443/inspector-protocol/network/resources/source.map`;
+  await loadResource(
+      redirectCrossOriginUrl,
+      `Response for fetch with redirect to cross-origin resource (should be blocked by CSP):`);
+
   testRunner.completeTest();
 })
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc b/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
index a226fce5..bf5c9d90 100644
--- a/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_network_resource_loader_browsertest.cc
@@ -136,13 +136,16 @@
   std::unique_ptr<protocol::DevToolsNetworkResourceLoader> CreateLoader(
       GURL url,
       protocol::DevToolsNetworkResourceLoader::Caching caching,
-      protocol::DevToolsNetworkResourceLoader::CompletionCallback callback) {
+      protocol::DevToolsNetworkResourceLoader::CompletionCallback callback,
+      protocol::DevToolsNetworkResourceLoader::RedirectCheckCallback
+          redirect_check_callback = {}) {
     return protocol::DevToolsNetworkResourceLoader::Create(
         CreateURLLoaderFactory(), std::move(url),
         current_frame_host()->GetLastCommittedOrigin(),
         current_frame_host()->ComputeSiteForCookies(), caching,
         protocol::DevToolsNetworkResourceLoader::Credentials::kInclude,
-        std::move(callback), current_frame_host()->IsOutermostMainFrame());
+        std::move(callback), current_frame_host()->IsOutermostMainFrame(),
+        std::move(redirect_check_callback));
   }
 
  protected:
@@ -302,4 +305,47 @@
   EXPECT_FALSE(request->load_flags & net::LOAD_BYPASS_CACHE);
 }
 
+IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest, RedirectAllowed) {
+  base::RunLoop run_loop;
+  const GURL source_map_url(
+      embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
+  const GURL redirect_url(embedded_test_server()->GetURL(
+      "a.com", "/server-redirect?" + source_map_url.spec()));
+
+  auto loader = CreateLoader(
+      redirect_url, protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
+      base::BindOnce(CheckSuccess, this, &run_loop),
+      base::BindRepeating([](const net::RedirectInfo&) { return true; }));
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_F(DevtoolsNetworkResourceLoaderTest,
+                       RedirectBlockedByCallback) {
+  base::RunLoop run_loop;
+  const GURL source_map_url(
+      embedded_test_server()->GetURL("a.com", "/devtools/source.map"));
+  const GURL redirect_url(embedded_test_server()->GetURL(
+      "a.com", "/server-redirect?" + source_map_url.spec()));
+
+  auto complete_callback = base::BindOnce(
+      [](base::RunLoop* run_loop,
+         protocol::DevToolsNetworkResourceLoader* loader,
+         const net::HttpResponseHeaders* rh, bool success, int net_error,
+         std::string content) {
+        EXPECT_FALSE(success);
+        EXPECT_EQ(net_error, net::ERR_BLOCKED_BY_CSP);
+        EXPECT_EQ(content, "");
+        EXPECT_TRUE(rh);
+        EXPECT_EQ(rh->response_code(), 301);
+        run_loop->Quit();
+      },
+      &run_loop);
+
+  auto loader = CreateLoader(
+      redirect_url, protocol::DevToolsNetworkResourceLoader::Caching::kDefault,
+      std::move(complete_callback),
+      base::BindRepeating([](const net::RedirectInfo&) { return false; }));
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
index 7c31915..4508dc0a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp-expected.txt
@@ -11,4 +11,21 @@
         success : true
     }
 }
+Response for fetch with redirect to same-origin resource (should be allowed by CSP): {
+    resource : {
+        headers : <object>
+        httpStatusCode : 200
+        stream : 2
+        success : true
+    }
+}
+Response for fetch with redirect to cross-origin resource (should be blocked by CSP): {
+    resource : {
+        headers : <object>
+        httpStatusCode : 302
+        netError : -30
+        netErrorName : net::ERR_BLOCKED_BY_CSP
+        success : false
+    }
+}
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
index 1fc7c8e0..f91479db 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/load-network-resource-csp.js
@@ -27,5 +27,17 @@
       sameOriginUrl,
       `Response for fetch with same-origin resource (should be allowed by CSP):`);
 
+  const redirectSameOriginUrl =
+      `http://127.0.0.1:8000/resources/redirect.php?url=http://127.0.0.1:8000/inspector-protocol/network/resources/source.map`;
+  await loadResource(
+      redirectSameOriginUrl,
+      `Response for fetch with redirect to same-origin resource (should be allowed by CSP):`);
+
+  const redirectCrossOriginUrl =
+      `http://127.0.0.1:8000/resources/redirect.php?url=https://localhost:8443/inspector-protocol/network/resources/source.map`;
+  await loadResource(
+      redirectCrossOriginUrl,
+      `Response for fetch with redirect to cross-origin resource (should be blocked by CSP):`);
+
   testRunner.completeTest();
 })
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.