0

Add Fetching Implementation with Cross-Partitioned Blob URL

Bug: 357484649
Change-Id: I074b15fd78114185f1d7bfddc2647e2dd76bbed7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5762280
Commit-Queue: Janice Liu <janiceliu@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Auto-Submit: Janice Liu <janiceliu@chromium.org>
Reviewed-by: David Baron <dbaron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1344948}
This commit is contained in:
Janice Liu
2024-08-21 18:39:10 +00:00
committed by Chromium LUCI CQ
parent 66aba9fe13
commit d9e47c7277
8 changed files with 211 additions and 16 deletions
storage/browser
third_party/blink/web_tests
VirtualTestSuites
virtual
block-cross-partition-blob-url-fetching

@ -47,6 +47,8 @@ component("browser") {
"blob/blob_url_store_impl.h",
"blob/blob_url_utils.cc",
"blob/blob_url_utils.h",
"blob/features.cc",
"blob/features.h",
"blob/mojo_blob_reader.cc",
"blob/mojo_blob_reader.h",
"blob/scoped_file.cc",

@ -14,6 +14,7 @@
#include "storage/browser/blob/blob_url_loader_factory.h"
#include "storage/browser/blob/blob_url_registry.h"
#include "storage/browser/blob/blob_url_utils.h"
#include "storage/browser/blob/features.h"
#include "url/url_util.h"
namespace storage {
@ -122,9 +123,21 @@ void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
std::move(callback).Run(std::nullopt, std::nullopt);
return;
}
if (base::FeatureList::IsEnabled(
features::kBlockCrossPartitionBlobUrlFetching) &&
!registry_->IsUrlMapped(BlobUrlUtils::ClearUrlFragment(url),
storage_key_)) {
BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
std::move(callback).Run(std::nullopt, std::nullopt);
return;
}
BlobURLLoaderFactory::Create(registry_->GetBlobFromUrl(url), url,
std::move(receiver));
// When a fragment URL is present, registry_->GetUnsafeAgentClusterID(url) and
// registry_->GetUnsafeTopLevelSite(url) will return nullopt because their
// implementations don't remove the fragment and only support fragmentless
// URLs (crbug.com/40775506).
std::move(callback).Run(registry_->GetUnsafeAgentClusterID(url),
registry_->GetUnsafeTopLevelSite(url));
}

@ -21,6 +21,7 @@
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/blob/blob_url_registry.h"
#include "storage/browser/blob/features.h"
#include "testing/gtest/include/gtest/gtest.h"
using blink::mojom::BlobURLStore;
@ -30,10 +31,9 @@ namespace storage {
namespace {
enum class PartitionedBlobUrlTestCase {
kPartitioningDisabledWithSupportDisabled,
kPartitioningDisabledWithSupportEnabled,
kPartitioningEnabledWithSupportDisabled,
kPartitioningEnabledWithSupportEnabled,
kPartitioningDisabled,
kBlockCrossPartitionBlobUrlFetchingEnabled,
kBlockCrossPartitionBlobUrlFetchingDisabled,
};
class BlobURLStoreImplTestP
@ -51,14 +51,17 @@ class BlobURLStoreImplTestP
&BlobURLStoreImplTestP::OnBadMessage, base::Unretained(this)));
}
void TearDown() override {
mojo::SetDefaultProcessErrorHandler(base::NullCallback());
}
void InitializeScopedFeatureList() {
std::vector<base::test::FeatureRef> enabled_features{};
std::vector<base::test::FeatureRef> disabled_features{};
if (BlockCrossPartitionBlobUrlFetchingEnabled()) {
enabled_features.push_back(features::kBlockCrossPartitionBlobUrlFetching);
} else {
disabled_features.push_back(
features::kBlockCrossPartitionBlobUrlFetching);
}
if (StoragePartitioningEnabled()) {
enabled_features.push_back(net::features::kThirdPartyStoragePartitioning);
} else {
@ -69,16 +72,26 @@ class BlobURLStoreImplTestP
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
bool StoragePartitioningEnabled() {
bool BlockCrossPartitionBlobUrlFetchingEnabled() {
switch (test_case_) {
case PartitionedBlobUrlTestCase::kPartitioningEnabledWithSupportDisabled:
case PartitionedBlobUrlTestCase::kPartitioningEnabledWithSupportEnabled:
case PartitionedBlobUrlTestCase::
kBlockCrossPartitionBlobUrlFetchingDisabled:
case PartitionedBlobUrlTestCase::
kBlockCrossPartitionBlobUrlFetchingEnabled:
return true;
default:
return false;
}
}
bool StoragePartitioningEnabled() {
return test_case_ != PartitionedBlobUrlTestCase::kPartitioningDisabled;
}
void TearDown() override {
mojo::SetDefaultProcessErrorHandler(base::NullCallback());
}
void OnBadMessage(const std::string& error) {
bad_messages_.push_back(error);
}
@ -386,6 +399,110 @@ TEST_P(BlobURLStoreImplTestP, ResolveAsURLLoaderFactory) {
download_loop.Run();
}
TEST_P(BlobURLStoreImplTestP,
ResolveAsURLLoaderFactoryWithSeparateStorageKeys) {
const blink::StorageKey kWrongStorageKey = blink::StorageKey::Create(
kOrigin, kWrongTopLevelSite, blink::mojom::AncestorChainBit::kCrossSite);
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store1(kStorageKey, kStorageKey.origin(), /*rph_id=*/0,
url_registry_.AsWeakPtr());
BlobURLStoreImpl url_store2(kWrongStorageKey, kStorageKey.origin(),
/*rph_id=*/0, url_registry_.AsWeakPtr());
RegisterURL(&url_store1, std::move(blob), kValidUrl);
mojo::Remote<network::mojom::URLLoaderFactory> factory;
base::RunLoop resolve_loop;
url_store2.ResolveAsURLLoaderFactory(
kValidUrl, factory.BindNewPipeAndPassReceiver(),
base::BindLambdaForTesting(
[&](const std::optional<base::UnguessableToken>&
unsafe_agent_cluster_id,
const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
if (BlockCrossPartitionBlobUrlFetchingEnabled()) {
EXPECT_FALSE(unsafe_agent_cluster_id.has_value());
EXPECT_FALSE(unsafe_top_level_site.has_value());
} else {
EXPECT_EQ(*unsafe_agent_cluster_id, agent_cluster_id_);
}
resolve_loop.Quit();
}));
resolve_loop.Run();
auto request = std::make_unique<network::ResourceRequest>();
request->url = kValidUrl;
auto loader = network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop download_loop;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
factory.get(), base::BindLambdaForTesting(
[&](std::unique_ptr<std::string> response_body) {
download_loop.Quit();
if (BlockCrossPartitionBlobUrlFetchingEnabled()) {
EXPECT_FALSE(response_body);
} else {
ASSERT_TRUE(response_body);
EXPECT_EQ("hello world", *response_body);
}
}));
download_loop.Run();
}
TEST_P(BlobURLStoreImplTestP, ResolveAsURLLoaderFactoryWithFragmentUrl) {
const blink::StorageKey kWrongStorageKey = blink::StorageKey::Create(
kOrigin, kWrongTopLevelSite, blink::mojom::AncestorChainBit::kCrossSite);
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
BlobURLStoreImpl url_store1(kWrongStorageKey, kStorageKey.origin(),
/*rph_id=*/0, url_registry_.AsWeakPtr());
mojo::Remote<BlobURLStore> url_store2(CreateURLStore());
RegisterURL(url_store2.get(), std::move(blob), kFragmentUrl);
mojo::Remote<network::mojom::URLLoaderFactory> factory;
base::RunLoop resolve_loop;
url_store1.ResolveAsURLLoaderFactory(
kFragmentUrl, factory.BindNewPipeAndPassReceiver(),
base::BindLambdaForTesting(
[&](const std::optional<base::UnguessableToken>&
unsafe_agent_cluster_id,
const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
// TODO(crbug.com/40775506): Fix fragment URL bug.
if (BlockCrossPartitionBlobUrlFetchingEnabled() ||
!StoragePartitioningEnabled()) {
EXPECT_FALSE(unsafe_agent_cluster_id.has_value());
EXPECT_FALSE(unsafe_top_level_site.has_value());
} else {
EXPECT_EQ(*unsafe_agent_cluster_id, agent_cluster_id_);
}
resolve_loop.Quit();
}));
resolve_loop.Run();
auto request = std::make_unique<network::ResourceRequest>();
request->url = kFragmentUrl;
auto loader = network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
base::RunLoop download_loop;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
factory.get(), base::BindLambdaForTesting(
[&](std::unique_ptr<std::string> response_body) {
download_loop.Quit();
if (BlockCrossPartitionBlobUrlFetchingEnabled() ||
!StoragePartitioningEnabled()) {
EXPECT_FALSE(response_body);
} else {
ASSERT_TRUE(response_body);
EXPECT_EQ("hello world", *response_body);
}
}));
download_loop.Run();
}
TEST_P(BlobURLStoreImplTestP, ResolveForNavigation) {
mojo::PendingRemote<blink::mojom::Blob> blob =
CreateBlobFromString(kId, "hello world");
@ -428,11 +545,22 @@ TEST_P(BlobURLStoreImplTestP, ResolveForNavigation) {
INSTANTIATE_TEST_SUITE_P(
BlobURLStoreImplTests,
BlobURLStoreImplTestP,
::testing::Values(
PartitionedBlobUrlTestCase::kPartitioningDisabledWithSupportDisabled,
PartitionedBlobUrlTestCase::kPartitioningDisabledWithSupportEnabled,
PartitionedBlobUrlTestCase::kPartitioningEnabledWithSupportDisabled,
PartitionedBlobUrlTestCase::kPartitioningEnabledWithSupportEnabled));
testing::Values(
PartitionedBlobUrlTestCase::kPartitioningDisabled,
PartitionedBlobUrlTestCase::kBlockCrossPartitionBlobUrlFetchingDisabled,
PartitionedBlobUrlTestCase::kBlockCrossPartitionBlobUrlFetchingEnabled),
[](const testing::TestParamInfo<PartitionedBlobUrlTestCase>& info) {
switch (info.param) {
case PartitionedBlobUrlTestCase::kPartitioningDisabled:
return "PartitioningDisabled";
case PartitionedBlobUrlTestCase::
kBlockCrossPartitionBlobUrlFetchingDisabled:
return "BlockCrossPartitionBlobUrlFetchingDisabled";
case PartitionedBlobUrlTestCase::
kBlockCrossPartitionBlobUrlFetchingEnabled:
return "BlockCrossPartitionBlobUrlFetchingEnabled";
}
});
} // namespace
} // namespace storage

@ -0,0 +1,16 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "storage/browser/blob/features.h"
namespace features {
// Please keep features in alphabetical order.
BASE_FEATURE(kBlockCrossPartitionBlobUrlFetching,
"BlockCrossPartitionBlobUrlFetching",
base::FEATURE_DISABLED_BY_DEFAULT);
// Please keep features in alphabetical order.
} // namespace features

@ -0,0 +1,22 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef STORAGE_BROWSER_BLOB_FEATURES_H_
#define STORAGE_BROWSER_BLOB_FEATURES_H_
#include "base/component_export.h"
#include "base/features.h"
namespace features {
// Please keep features in alphabetical order.
// Enables blob URL fetches to fail when cross-partition.
COMPONENT_EXPORT(STORAGE_BROWSER)
BASE_DECLARE_FEATURE(kBlockCrossPartitionBlobUrlFetching);
// Please keep features in alphabetical order.
} // namespace features
#endif // STORAGE_BROWSER_BLOB_FEATURES_H_

@ -1150,6 +1150,16 @@
"owners": ["arichiv@chromium.org", "bingler@chromium.org"]
},
{
"prefix": "block-cross-partition-blob-url-fetching",
"platforms": ["Linux"],
"bases": [
"external/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html"],
"args": ["--enable-features=BlockCrossPartitionBlobUrlFetching"],
"expires": "Feb 4, 2025",
"owners": ["janiceliu@chromium.org"]
},
"isInputPending requires threaded compositing and layerized iframes, so ",
"these tests are run exclusively with a virtual test suite that never ",
"expires.",

@ -0,0 +1 @@
This directory is for tests that need the Block Cross Partition Blob URL Fetching feature enabled.

@ -0,0 +1,3 @@
This is a testharness.js-based test.
Harness: the test ran to completion.