Chrome · WebAppInstalls
CVE-2026-113024
UAF in WebAppInstalls
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
GetURLLoaderFactorychrome/browser/web_applications/web_install_service_impl.cc |
modified | |
ifchrome/browser/web_applications/web_install_service_impl.cc |
modified | |
WebContentschrome/browser/web_applications/web_install_service_impl.h |
modified | |
SharedURLLoaderFactorychrome/browser/web_applications/web_install_service_impl.h |
modified | |
WebInstallFromManifestLocalNetworkBrowserTestchrome/browser/web_applications/web_install_from_manifest_browsertest.cc |
modified | |
TEST_Fchrome/browser/web_applications/web_install_service_impl_unittest.cc |
modified |
Files Changed
chrome/browser/web_applications/web_install_manifest_fetcher.hchrome/browser/web_applications/web_install_service_impl.ccchrome/browser/web_applications/web_install_manifest_fetcher_unittest.ccchrome/browser/web_applications/web_install_service_impl.hchrome/browser/web_applications/BUILD.gnchrome/browser/web_applications/web_install_from_manifest_browsertest.ccchrome/browser/web_applications/web_install_service_impl_unittest.ccchrome/browser/web_applications/web_install_manifest_fetcher.cc
Patch
From 0e5abc0e8df21048fc4a9779de0ea809e1b8abfe Mon Sep 17 00:00:00 2001
From: Kunjan Vinay Patel <ksukh@google.com>
Date: Thu, 17 Sep 2026 12:47:55 -0700
Subject: [PATCH] Enforce address space checks on Web Install manifest fetches
The Web Install API fetches the application manifest in the browser
process on behalf of the initiating document. Previously, the request
used the profile's default URLLoaderFactory without frame-bound client
state, preventing the network service from applying Local Network Access
rules.
This change uses RenderFrameHost::CreateNetworkServiceDefaultFactory()
to issue manifest fetches using a frame-bound URLLoaderFactory
configured with the initiating document's client security state and
network isolation info. In addition, insecure contexts are rejected up
front in CreateIfAllowed().
Bug: 533043403, 550507869, 556141831
Change-Id: Id7f8eaf0b258ef11b7597b03278f65a4766d0e84
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8410644
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Kunjan Patel <ksukh@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1700052}
---
diff --git a/chrome/browser/web_applications/web_install_manifest_fetcher.cc b/chrome/browser/web_applications/web_install_manifest_fetcher.cc
index 6707257d..ecb7b38 100644
--- a/chrome/browser/web_applications/web_install_manifest_fetcher.cc
+++ b/chrome/browser/web_applications/web_install_manifest_fetcher.cc
@@ -33,8 +33,10 @@
WebInstallManifestFetcher::WebInstallManifestFetcher(
GURL manifest_url,
+ url::Origin initiator_origin,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: manifest_url_(std::move(manifest_url)),
+ initiator_origin_(std::move(initiator_origin)),
url_loader_factory_(std::move(url_loader_factory)) {
CHECK(manifest_url_.SchemeIs(url::kHttpsScheme) ||
(manifest_url_.SchemeIs(url::kHttpScheme) &&
@@ -91,6 +93,7 @@
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = manifest_url_;
+ resource_request->request_initiator = initiator_origin_;
resource_request->method = "GET";
// Cookies are not allowed.
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
diff --git a/chrome/browser/web_applications/web_install_manifest_fetcher.h b/chrome/browser/web_applications/web_install_manifest_fetcher.h
index 8d579eb9..0551f40 100644
--- a/chrome/browser/web_applications/web_install_manifest_fetcher.h
+++ b/chrome/browser/web_applications/web_install_manifest_fetcher.h
@@ -17,6 +17,7 @@
#include "base/types/expected.h"
#include "chrome/browser/web_applications/model/web_install_manifest_fetch_error.h"
#include "url/gurl.h"
+#include "url/origin.h"
namespace net {
struct RedirectInfo;
@@ -43,6 +44,7 @@
WebInstallManifestFetcher(
GURL manifest_url,
+ url::Origin initiator_origin,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~WebInstallManifestFetcher();
@@ -67,6 +69,7 @@
void OnManifestDownloaded(std::optional<std::string> manifest_content);
GURL manifest_url_;
+ url::Origin initiator_origin_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
FetchCallback fetch_callback_;
diff --git a/chrome/browser/web_applications/web_install_service_impl.cc b/chrome/browser/web_applications/web_install_service_impl.cc
index d2a0365..a30630c 100644
--- a/chrome/browser/web_applications/web_install_service_impl.cc
+++ b/chrome/browser/web_applications/web_install_service_impl.cc
@@ -59,6 +59,10 @@
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/manifest/manifest_util.h"
@@ -88,6 +92,7 @@
// Rate limiting defaults for cross-origin IsInstalled queries.
size_t g_max_cross_origin_queries = 100;
base::TimeDelta g_min_cross_origin_query_interval = base::Seconds(1);
+network::SharedURLLoaderFactory* g_test_factory = nullptr;
bool IsUrlAllowedForWebInstall(const GURL& url) {
return url.SchemeIs(url::kHttpsScheme) ||
@@ -241,6 +246,11 @@
return;
}
+ if (!network::IsUrlPotentiallyTrustworthy(
+ render_frame_host->GetLastCommittedURL())) {
+ return;
+ }
+
// TODO(crbug.com/493534965): Evaluate sandbox restrictions. In the meantime,
// Web Install API is not available in any sandboxed contexts, including
// sandboxed top-level documents, as well as frames with sandbox flags
@@ -269,6 +279,26 @@
interval);
}
+// static
+base::AutoReset<network::SharedURLLoaderFactory*>
+WebInstallServiceImpl::SetURLLoaderFactoryForTesting( // IN-TEST
+ network::SharedURLLoaderFactory* factory) {
+ return base::AutoReset<network::SharedURLLoaderFactory*>(&g_test_factory,
+ factory);
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+WebInstallServiceImpl::GetURLLoaderFactory() {
+ if (g_test_factory) {
+ return base::WrapRefCounted(g_test_factory);
+ }
+ mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
+ render_frame_host().CreateNetworkServiceDefaultFactory(
+ factory_remote.InitWithNewPipeAndPassReceiver());
+ return base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
+ std::move(factory_remote));
+}
+
void WebInstallServiceImpl::IsInstalled(
blink::mojom::ManifestInstallOptionsPtr options,
IsInstalledCallback callback) {
@@ -542,7 +572,8 @@
// DataErrors (invalid JSON, missing id) are returned accurately. This
// prevents sites from using error differences to detect Incognito mode.
manifest_fetcher_ = std::make_unique<WebInstallManifestFetcher>(
- install_target, initiating_profile->GetURLLoaderFactory());
+ install_target, render_frame_host().GetLastCommittedOrigin(),
+ GetURLLoaderFactory());
manifest_fetcher_->Fetch(base::BindOnce(
&WebInstallServiceImpl::OnManifestFetched, weak_ptr_factory_.GetWeakPtr(),
diff --git a/chrome/browser/web_applications/web_install_service_impl.h b/chrome/browser/web_applications/web_install_service_impl.h
index ab2e679..0701b35 100644
--- a/chrome/browser/web_applications/web_install_service_impl.h
+++ b/chrome/browser/web_applications/web_install_service_impl.h
@@ -30,6 +30,10 @@
class WebContents;
} // namespace content
+namespace network {
+class SharedURLLoaderFactory;
+} // namespace network
+
namespace webapps {
enum class InstallResultCode;
enum class InstallableStatusCode;
@@ -100,6 +104,8 @@
size_t max_queries);
static base::AutoReset<base::TimeDelta>
SetMinCrossOriginQueryIntervalForTesting(base::TimeDelta interval);
+ static base::AutoReset<network::SharedURLLoaderFactory*>
+ SetURLLoaderFactoryForTesting(network::SharedURLLoaderFactory* factory);
// blink::mojom::WebInstallService:
// TODO(crbug.com/485281836): Only explicit manifest IDs are supported for
@@ -116,6 +122,8 @@
// content::WebContentsObserver:
void PrimaryPageChanged(content::Page& page) override;
+ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
+
private:
// Internal entry point for the manifest URL install flow. Acquires the
// install-in-progress guard and wraps the callback so the guard is
diff --git a/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc b/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
index 7465fdfa..e43c30b 100644
--- a/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
+++ b/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
@@ -15,6 +15,7 @@
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
@@ -22,6 +23,7 @@
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/test/with_feature_override.h"
+#include "base/thread_annotations.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
@@ -74,6 +76,8 @@
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/ip_address_space_overrides_test_utils.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
#include "skia/ext/image_operations.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features_generated.h"
@@ -405,6 +409,73 @@
ukm::SourceIdType::APP_ID);
}
+// Fixture with a dedicated manifest server placed in the local network
+// address space, while the page servers stay in the public address space (as
+// the test harness configures by default).
+class WebInstallFromManifestLocalNetworkBrowserTest
+ : public WebInstallFromManifestBrowserTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ WebInstallFromManifestBrowserTest::SetUpCommandLine(command_line);
+ manifest_server_.AddDefaultHandlers(GetChromeTestDataDir());
+ manifest_server_.RegisterRequestHandler(base::BindRepeating(
+ &WebInstallFromManifestLocalNetworkBrowserTest::RecordManifestRequest,
+ base::Unretained(this)));
+ ASSERT_TRUE(manifest_server_.InitializeAndListen());
+ // Keep every other test server endpoint in the public address space, as
+ // the test harness does by default, while placing the manifest server on
+ // the local network.
+ network::AddIpAddressSpaceOverridesToCommandLine(
+ {network::GenerateIpAddressSpaceOverride(
+ manifest_server_, network::mojom::IPAddressSpace::kLocal),
+ "127.0.0.1:0=public", "[::1]:0=public"},
+ *command_line);
+ }
+
+ void SetUpOnMainThread() override {
+ WebInstallFromManifestBrowserTest::SetUpOnMainThread();
+ manifest_server_.StartAcceptingConnections();
+ }
+
+ // Returns whether the manifest server received any request.
+ bool ManifestServerSawRequest() {
+ base::AutoLock lock(manifest_request_count_lock_);
+ return manifest_request_count_ > 0;
+ }
+
+ protected:
+ net::EmbeddedTestServer manifest_server_;
+
+ private:
+ std::unique_ptr<net::test_server::HttpResponse> RecordManifestRequest(
+ const net::test_server::HttpRequest& request) {
+ base::AutoLock lock(manifest_request_count_lock_);
+ manifest_request_count_++;
+ // Fall through to the default handlers.
+ return nullptr;
+ }
+
+ base::Lock manifest_request_count_lock_;
+ int manifest_request_count_ GUARDED_BY(manifest_request_count_lock_) = 0;
+};
+
+// The manifest is fetched in the browser process on behalf of the initiating
+// document. A document in the public address space must not retrieve a
+// manifest from a host on the local network: the fetch is denied before any
+// request reaches the manifest host, and the install reports a DataError.
+IN_PROC_BROWSER_TEST_F(WebInstallFromManifestLocalNetworkBrowserTest,
+ ManifestOnLocalNetworkRejected) {
+ NavigateToValidUrl();
+ SetPermissionResponse(/*permission_granted=*/false);
+
+ ASSERT_TRUE(
+ TryInstallFromManifest(manifest_server_.GetURL(kValidManifestWithId)));
+
+ EXPECT_FALSE(ManifestServerSawRequest());
+ ASSERT_TRUE(ErrorExists());
+ EXPECT_EQ(GetErrorName(), kDataError);
+}
+
// Valid manifest with custom id, matching id option.
IN_PROC_BROWSER_TEST_F(WebInstallFromManifestBrowserTest,
ManifestAndId_Succeeds) {
diff --git a/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc b/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
index 27fc926..3ef6868 100644
--- a/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
+++ b/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
@@ -79,6 +79,7 @@
AddResponse(kManifestUrl, kValidManifestJson);
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -99,6 +100,7 @@
network::URLLoaderCompletionStatus(net::ERR_CONNECTION_REFUSED));
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -114,6 +116,7 @@
AddRedirect(kRedirectUrl, kRedirectTargetUrl);
WebInstallManifestFetcher fetcher(GURL(kRedirectUrl),
+ url::Origin::Create(GURL(kRedirectUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -129,6 +132,7 @@
test_factory_.AddResponse(std::string(kManifestUrl), "", net::HTTP_NOT_FOUND);
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -144,6 +148,7 @@
AddResponse(kManifestUrl, "");
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -165,6 +170,7 @@
AddResponse(kManifestUrl, std::string(100, 'x'));
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -184,7 +190,8 @@
}));
auto fetcher = std::make_unique<WebInstallManifestFetcher>(
- GURL(kManifestUrl), shared_url_loader_factory_);
+ GURL(kManifestUrl), url::Origin::Create(GURL(kManifestUrl)),
+ shared_url_loader_factory_);
bool callback_called = false;
fetcher->Fetch(base::BindLambdaForTesting(
diff --git a/chrome/browser/web_applications/web_install_service_impl_unittest.cc b/chrome/browser/web_applications/web_install_service_impl_unittest.cc
index 1c5bddc1..6c6a623 100644
--- a/chrome/browser/web_applications/web_install_service_impl_unittest.cc
+++ b/chrome/browser/web_applications/web_install_service_impl_unittest.cc
@@ -8,6 +8,7 @@
#include <optional>
#include "base/functional/callback_helpers.h"
+#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
@@ -32,10 +33,15 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
#include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/client_security_state.mojom.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -109,6 +115,9 @@
// retrievers know which page state to use.
fake_web_contents_manager().SetUrlLoaded(web_contents(),
GURL(kDocumentUrl));
+ test_url_loader_factory_reset_ =
+ WebInstallServiceImpl::SetURLLoaderFactoryForTesting(
+ profile_url_loader_factory().GetSafeWeakWrapper().get());
}
// Creates a WebInstallServiceImpl bound to `service_remote_` via the
@@ -203,6 +212,8 @@
private:
mojo::Remote<blink::mojom::WebInstallService> service_remote_;
base::test::ScopedFeatureList scoped_feature_list_;
+ std::optional<base::AutoReset<network::SharedURLLoaderFactory*>>
+ test_url_loader_factory_reset_;
};
///////////////////////////////////////////////////////////////////////////////
@@ -458,6 +469,21 @@
EXPECT_FALSE(remote.is_connected());
}
+// CreateIfAllowed with an untrustworthy origin resets the receiver.
+TEST_F(WebInstallServiceImplTest, CreateIfAllowed_UntrustworthyOrigin) {
+ // Navigate to an insecure HTTP URL.
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc b/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
index 7465fdfa..e43c30b 100644
--- a/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
+++ b/chrome/browser/web_applications/web_install_from_manifest_browsertest.cc
@@ -15,6 +15,7 @@
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
@@ -22,6 +23,7 @@
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/test/with_feature_override.h"
+#include "base/thread_annotations.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
@@ -74,6 +76,8 @@
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/ip_address_space_overrides_test_utils.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
#include "skia/ext/image_operations.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features_generated.h"
@@ -405,6 +409,73 @@
ukm::SourceIdType::APP_ID);
}
+// Fixture with a dedicated manifest server placed in the local network
+// address space, while the page servers stay in the public address space (as
+// the test harness configures by default).
+class WebInstallFromManifestLocalNetworkBrowserTest
+ : public WebInstallFromManifestBrowserTest {
+ public:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ WebInstallFromManifestBrowserTest::SetUpCommandLine(command_line);
+ manifest_server_.AddDefaultHandlers(GetChromeTestDataDir());
+ manifest_server_.RegisterRequestHandler(base::BindRepeating(
+ &WebInstallFromManifestLocalNetworkBrowserTest::RecordManifestRequest,
+ base::Unretained(this)));
+ ASSERT_TRUE(manifest_server_.InitializeAndListen());
+ // Keep every other test server endpoint in the public address space, as
+ // the test harness does by default, while placing the manifest server on
+ // the local network.
+ network::AddIpAddressSpaceOverridesToCommandLine(
+ {network::GenerateIpAddressSpaceOverride(
+ manifest_server_, network::mojom::IPAddressSpace::kLocal),
+ "127.0.0.1:0=public", "[::1]:0=public"},
+ *command_line);
+ }
+
+ void SetUpOnMainThread() override {
+ WebInstallFromManifestBrowserTest::SetUpOnMainThread();
+ manifest_server_.StartAcceptingConnections();
+ }
+
+ // Returns whether the manifest server received any request.
+ bool ManifestServerSawRequest() {
+ base::AutoLock lock(manifest_request_count_lock_);
+ return manifest_request_count_ > 0;
+ }
+
+ protected:
+ net::EmbeddedTestServer manifest_server_;
+
+ private:
+ std::unique_ptr<net::test_server::HttpResponse> RecordManifestRequest(
+ const net::test_server::HttpRequest& request) {
+ base::AutoLock lock(manifest_request_count_lock_);
+ manifest_request_count_++;
+ // Fall through to the default handlers.
+ return nullptr;
+ }
+
+ base::Lock manifest_request_count_lock_;
+ int manifest_request_count_ GUARDED_BY(manifest_request_count_lock_) = 0;
+};
+
+// The manifest is fetched in the browser process on behalf of the initiating
+// document. A document in the public address space must not retrieve a
+// manifest from a host on the local network: the fetch is denied before any
+// request reaches the manifest host, and the install reports a DataError.
+IN_PROC_BROWSER_TEST_F(WebInstallFromManifestLocalNetworkBrowserTest,
+ ManifestOnLocalNetworkRejected) {
+ NavigateToValidUrl();
+ SetPermissionResponse(/*permission_granted=*/false);
+
+ ASSERT_TRUE(
+ TryInstallFromManifest(manifest_server_.GetURL(kValidManifestWithId)));
+
+ EXPECT_FALSE(ManifestServerSawRequest());
+ ASSERT_TRUE(ErrorExists());
+ EXPECT_EQ(GetErrorName(), kDataError);
+}
+
// Valid manifest with custom id, matching id option.
IN_PROC_BROWSER_TEST_F(WebInstallFromManifestBrowserTest,
ManifestAndId_Succeeds) {
diff --git a/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc b/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
index 27fc926..3ef6868 100644
--- a/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
+++ b/chrome/browser/web_applications/web_install_manifest_fetcher_unittest.cc
@@ -79,6 +79,7 @@
AddResponse(kManifestUrl, kValidManifestJson);
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -99,6 +100,7 @@
network::URLLoaderCompletionStatus(net::ERR_CONNECTION_REFUSED));
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -114,6 +116,7 @@
AddRedirect(kRedirectUrl, kRedirectTargetUrl);
WebInstallManifestFetcher fetcher(GURL(kRedirectUrl),
+ url::Origin::Create(GURL(kRedirectUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -129,6 +132,7 @@
test_factory_.AddResponse(std::string(kManifestUrl), "", net::HTTP_NOT_FOUND);
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -144,6 +148,7 @@
AddResponse(kManifestUrl, "");
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -165,6 +170,7 @@
AddResponse(kManifestUrl, std::string(100, 'x'));
WebInstallManifestFetcher fetcher(GURL(kManifestUrl),
+ url::Origin::Create(GURL(kManifestUrl)),
shared_url_loader_factory_);
base::test::TestFuture<
base::expected<std::string, WebInstallManifestFetchError>>
@@ -184,7 +190,8 @@
}));
auto fetcher = std::make_unique<WebInstallManifestFetcher>(
- GURL(kManifestUrl), shared_url_loader_factory_);
+ GURL(kManifestUrl), url::Origin::Create(GURL(kManifestUrl)),
+ shared_url_loader_factory_);
bool callback_called = false;
fetcher->Fetch(base::BindLambdaForTesting(
diff --git a/chrome/browser/web_applications/web_install_service_impl_unittest.cc b/chrome/browser/web_applications/web_install_service_impl_unittest.cc
index 1c5bddc1..6c6a623 100644
--- a/chrome/browser/web_applications/web_install_service_impl_unittest.cc
+++ b/chrome/browser/web_applications/web_install_service_impl_unittest.cc
@@ -8,6 +8,7 @@
#include <optional>
#include "base/functional/callback_helpers.h"
+#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
@@ -32,10 +33,15 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
#include "mojo/public/cpp/test_support/test_utils.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/public/mojom/client_security_state.mojom.h"
+#include "services/network/public/mojom/ip_address_space.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -109,6 +115,9 @@
// retrievers know which page state to use.
fake_web_contents_manager().SetUrlLoaded(web_contents(),
GURL(kDocumentUrl));
+ test_url_loader_factory_reset_ =
+ WebInstallServiceImpl::SetURLLoaderFactoryForTesting(
+ profile_url_loader_factory().GetSafeWeakWrapper().get());
}
// Creates a WebInstallServiceImpl bound to `service_remote_` via the
@@ -203,6 +212,8 @@
private:
mojo::Remote<blink::mojom::WebInstallService> service_remote_;
base::test::ScopedFeatureList scoped_feature_list_;
+ std::optional<base::AutoReset<network::SharedURLLoaderFactory*>>
+ test_url_loader_factory_reset_;
};
///////////////////////////////////////////////////////////////////////////////
@@ -458,6 +469,21 @@
EXPECT_FALSE(remote.is_connected());
}
+// CreateIfAllowed with an untrustworthy origin resets the receiver.
+TEST_F(WebInstallServiceImplTest, CreateIfAllowed_UntrustworthyOrigin) {
+ // Navigate to an insecure HTTP URL.
+ NavigateAndCommit(GURL("http://insecure.example.com"));
+
+ mojo::Remote<blink::mojom::WebInstallService> remote;
+ WebInstallServiceImpl::CreateIfAllowed(web_contents()->GetPrimaryMainFrame(),
+ remote.BindNewPipeAndPassReceiver());
+
+ base::test::TestFuture<blink::mojom::WebInstallServiceResult> future;
+ remote->InstallFromManifest(/*options=*/nullptr, future.GetCallback());
+ remote.FlushForTesting();
+ EXPECT_FALSE(remote.is_connected());
+}
+
// Direct coverage for the CreateWebAppFromManifest() early-return guards in
// web_app_dialog_utils.cc (the mojo service path bypasses this helper). Each
// test arms one guard and asserts the callback runs exactly once (TestFuture's
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.
References
On This Page