High chrome Logic Error 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Loader
DescriptionInsufficient validation of untrusted input in Loader
ComponentLoader
Bug ClassLogic Error
Tracker500554346
Fix commit8fcc779829bf (chromium/src) +93/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc
modified
TEST
content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
modified

Files Changed

  • content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc
  • content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
From 8fcc779829bf7106ee8e02493ace0c0ff435fd36 Mon Sep 17 00:00:00 2001
From: Minoru Chikamune <[email protected]>
Date: Tue, 02 Jun 2026 17:48:48 -0700
Subject: [PATCH] Bugfix: This CL fixes a bug reported in crbug.com/500554346

[analysis]
https://docs.google.com/document/d/1Eo_0xvh-g7x0f_DyNhbYTSWbW1Gpdb3JQOv6XKqGtkU/edit?resourcekey=0-x0nLWecQ0NBc05J76n592g&tab=t.0#heading=h.4ouk0evxdnbl

[reasoning]
https://docs.google.com/document/d/116U7-f0JMNXnubCLB2t2duFOkpOFyM3Mm9t5Dqw0Ppg/edit?resourcekey=0-LhL0UerWZJhm0HDpLv9zmg&tab=t.0

Bug: 500554346
Change-Id: Icdd5a534474c03b1050d4b5521e74f29f614ec86
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7894356
Reviewed-by: Kouhei Ueno <[email protected]>
Commit-Queue: Minoru Chikamune <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1640599}
---

diff --git a/content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc b/content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc
index 776e0f0..a5abe67 100644
--- a/content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc
+++ b/content/browser/web_package/subresource_signed_exchange_url_loader_factory.cc
@@ -73,6 +73,17 @@
     const network::ResourceRequest& request,
     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  if (request.mode == network::mojom::RequestMode::kNavigate) {
+    network::debug::ScopedResourceRequestCrashKeys request_crash_keys(request);
+    mojo::ReportBadMessage(
+        "SubresourceSignedExchangeURLLoaderFactory: "
+        "kNavigate mode is forbidden for subresources");
+    mojo::Remote<network::mojom::URLLoaderClient>(std::move(client))
+        ->OnComplete(
+            network::URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
+    return;
+  }
+
   if (!IsValidRequestInitiator(request, request_initiator_origin_lock_)) {
     network::debug::ScopedResourceRequestCrashKeys request_crash_keys(request);
     network::debug::ScopedRequestInitiatorOriginLockCrashKey lock_crash_keys(
diff --git a/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc b/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
index 87ba0601..c05f36d 100644
--- a/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
@@ -12,12 +12,14 @@
 #include "base/test/bind.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "mojo/public/cpp/system/functions.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/test/test_url_loader_client.h"
+#include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -172,5 +174,85 @@
   mojo::SetDefaultProcessErrorHandler(base::NullCallback());
 }
 
+std::unique_ptr<PrefetchedSignedExchangeCacheEntry> CreateWorkingCacheEntry(
+    const GURL& outer_url,
+    const GURL& inner_url,
+    const std::string& inner_body,
+    storage::BlobStorageContext* blob_context) {
+  auto entry = std::make_unique<PrefetchedSignedExchangeCacheEntry>();
+  auto status = std::make_unique<network::URLLoaderCompletionStatus>();
+  entry->SetCompletionStatus(std::move(status));
+  entry->SetOuterUrl(outer_url);
+  entry->SetInnerUrl(inner_url);
+  auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+      net::HttpUtil::AssembleRawHeaders(
+          "HTTP/1.1 200 OK\nContent-type: application/custom\n\n"));
+  auto outer_response = network::mojom::URLResponseHead::New();
+  outer_response->headers = headers;
+  entry->SetOuterResponse(std::move(outer_response));
+  auto header_integrity = std::make_unique<net::SHA256HashValue>();
+  entry->SetHeaderIntegrity(std::move(header_integrity));
+  auto inner_response = network::mojom::URLResponseHead::New();
+  inner_response->headers = headers;
+  entry->SetInnerResponse(std::move(inner_response));
+
+  auto builder = std::make_unique<storage::BlobDataBuilder>("working_uuid");
+  builder->AppendData(inner_body);
+  std::unique_ptr<storage::BlobDataHandle> blob_handle =
+      blob_context->AddFinishedBlob(std::move(builder));
+
+  entry->SetBlobDataHandle(std::move(blob_handle));
+  entry->SetSignatureExpireTime(base::Time::Now() + base::Days(1));
+  return entry;
+}
+
+TEST(SubresourceSignedExchangeURLLoaderFactoryTest, CorsBypassViaKNavigate) {
+  BrowserTaskEnvironment task_environment;
+  GURL inner_url("https://target.com/secret.json");
+  GURL outer_url("https://target.com/outer");
+  auto attacker_origin = url::Origin::Create(GURL("https://attacker.com"));
+
+  storage::BlobStorageContext blob_context;
+  std::string secret_data = "flag{bypass}";
+  auto entry =
+      CreateWorkingCacheEntry(outer_url, inner_url, secret_data, &blob_context);
+
+  mojo::Remote<network::mojom::URLLoaderFactory> factory;
+  new content::SubresourceSignedExchangeURLLoaderFactory(
+      factory.BindNewPipeAndPassReceiver(), std::move(entry),
+      /*request_initiator_origin_lock=*/attacker_origin);
+
+  std::string received_error;
+  mojo::SetDefaultProcessErrorHandler(base::BindLambdaForTesting(
+      [&](const std::string& error) { received_error = error; }));
+
+  mojo::Remote<network::mojom::URLLoader> loader;
+  network::TestURLLoaderClient client;
+  network::ResourceRequest request;
+  request.url = inner_url;
+  request.request_initiator = attacker_origin;
+
+  // Using kNavigate bypasses CORS and ORB checks in
+  // SignedExchangeInnerResponseURLLoader
+  request.mode = network::mojom::RequestMode::kNavigate;
+
+  factory->CreateLoaderAndStart(
+      loader.BindNewPipeAndPassReceiver(), 123,
+      network::mojom::kURLLoadOptionNone, request, client.CreateRemote(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client.RunUntilComplete();
+
+  // The request should be rejected with net::ERR_INVALID_ARGUMENT.
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client.completion_status().error_code);
+
+  factory.FlushForTesting();
+  EXPECT_EQ(received_error,
+            "SubresourceSignedExchangeURLLoaderFactory: "
+            "kNavigate mode is forbidden for subresources");
+
+  mojo::SetDefaultProcessErrorHandler(base::NullCallback());
+}
+
 }  // namespace
 }  // namespace content
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc b/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
index 87ba0601..c05f36d 100644
--- a/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/subresource_signed_exchange_url_loader_factory_unittest.cc
@@ -12,12 +12,14 @@
 #include "base/test/bind.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "mojo/public/cpp/system/functions.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/test/test_url_loader_client.h"
+#include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -172,5 +174,85 @@
   mojo::SetDefaultProcessErrorHandler(base::NullCallback());
 }
 
+std::unique_ptr<PrefetchedSignedExchangeCacheEntry> CreateWorkingCacheEntry(
+    const GURL& outer_url,
+    const GURL& inner_url,
+    const std::string& inner_body,
+    storage::BlobStorageContext* blob_context) {
+  auto entry = std::make_unique<PrefetchedSignedExchangeCacheEntry>();
+  auto status = std::make_unique<network::URLLoaderCompletionStatus>();
+  entry->SetCompletionStatus(std::move(status));
+  entry->SetOuterUrl(outer_url);
+  entry->SetInnerUrl(inner_url);
+  auto headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+      net::HttpUtil::AssembleRawHeaders(
+          "HTTP/1.1 200 OK\nContent-type: application/custom\n\n"));
+  auto outer_response = network::mojom::URLResponseHead::New();
+  outer_response->headers = headers;
+  entry->SetOuterResponse(std::move(outer_response));
+  auto header_integrity = std::make_unique<net::SHA256HashValue>();
+  entry->SetHeaderIntegrity(std::move(header_integrity));
+  auto inner_response = network::mojom::URLResponseHead::New();
+  inner_response->headers = headers;
+  entry->SetInnerResponse(std::move(inner_response));
+
+  auto builder = std::make_unique<storage::BlobDataBuilder>("working_uuid");
+  builder->AppendData(inner_body);
+  std::unique_ptr<storage::BlobDataHandle> blob_handle =
+      blob_context->AddFinishedBlob(std::move(builder));
+
+  entry->SetBlobDataHandle(std::move(blob_handle));
+  entry->SetSignatureExpireTime(base::Time::Now() + base::Days(1));
+  return entry;
+}
+
+TEST(SubresourceSignedExchangeURLLoaderFactoryTest, CorsBypassViaKNavigate) {
+  BrowserTaskEnvironment task_environment;
+  GURL inner_url("https://target.com/secret.json");
+  GURL outer_url("https://target.com/outer");
+  auto attacker_origin = url::Origin::Create(GURL("https://attacker.com"));
+
+  storage::BlobStorageContext blob_context;
+  std::string secret_data = "flag{bypass}";
+  auto entry =
+      CreateWorkingCacheEntry(outer_url, inner_url, secret_data, &blob_context);
+
+  mojo::Remote<network::mojom::URLLoaderFactory> factory;
+  new content::SubresourceSignedExchangeURLLoaderFactory(
+      factory.BindNewPipeAndPassReceiver(), std::move(entry),
+      /*request_initiator_origin_lock=*/attacker_origin);
+
+  std::string received_error;
+  mojo::SetDefaultProcessErrorHandler(base::BindLambdaForTesting(
+      [&](const std::string& error) { received_error = error; }));
+
+  mojo::Remote<network::mojom::URLLoader> loader;
+  network::TestURLLoaderClient client;
+  network::ResourceRequest request;
+  request.url = inner_url;
+  request.request_initiator = attacker_origin;
+
+  // Using kNavigate bypasses CORS and ORB checks in
+  // SignedExchangeInnerResponseURLLoader
+  request.mode = network::mojom::RequestMode::kNavigate;
+
+  factory->CreateLoaderAndStart(
+      loader.BindNewPipeAndPassReceiver(), 123,
+      network::mojom::kURLLoadOptionNone, request, client.CreateRemote(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client.RunUntilComplete();
+
+  // The request should be rejected with net::ERR_INVALID_ARGUMENT.
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client.completion_status().error_code);
+
+  factory.FlushForTesting();
+  EXPECT_EQ(received_error,
+            "SubresourceSignedExchangeURLLoaderFactory: "
+            "kNavigate mode is forbidden for subresources");
+
+  mojo::SetDefaultProcessErrorHandler(base::NullCallback());
+}
+
 }  // namespace
 }  // namespace content
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.