Commit 1e679b77 authored by Hayato Ito's avatar Hayato Ito Committed by Chromium LUCI CQ

Support CORB in WebBundle subresource loading

Enable CORB protection for subresource loading in WebBundle.

WebBundle design doc is here.
https://docs.google.com/document/d/1_AqUBS4Gr45MPPtXGTUl7Q0DMIEw2zUeWO0zo6Sj0z4/edit#heading=h.pi5jgk8nqwh4

This implementation uses CrossOriginReadBlocking::ResponseAnalyzer to
decide whether to block resources or not, as network::URLLoader does.

Bug: 1082020,1149258
Change-Id: I6aa53db5fc0839e8a74bf834da2d26a017d2e240
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2585250Reviewed-by: default avatarKunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarŁukasz Anforowicz <lukasza@chromium.org>
Commit-Queue: Hayato Ito <hayato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841389}
parent 36f403a4
...@@ -1632,4 +1632,110 @@ IN_PROC_BROWSER_TEST_F(ContentBrowserTest, CorpVsBrowserInitiatedRequest) { ...@@ -1632,4 +1632,110 @@ IN_PROC_BROWSER_TEST_F(ContentBrowserTest, CorpVsBrowserInitiatedRequest) {
LoadBasicRequest(partition->GetNetworkContext(), test_url)); LoadBasicRequest(partition->GetNetworkContext(), test_url));
} }
// This test class sets up a link element for webbundle subresource loading.
// e.g. <link rel=webbundle href=".../foo.wbn" resources="...">.
class CrossSiteDocumentBlockingWebBundleTest
: public CrossSiteDocumentBlockingTestBase {
public:
CrossSiteDocumentBlockingWebBundleTest() {
scoped_feature_list_.InitAndEnableFeature(features::kSubresourceWebBundles);
}
~CrossSiteDocumentBlockingWebBundleTest() override = default;
CrossSiteDocumentBlockingWebBundleTest(
const CrossSiteDocumentBlockingWebBundleTest&) = delete;
CrossSiteDocumentBlockingWebBundleTest& operator=(
const CrossSiteDocumentBlockingWebBundleTest&) = delete;
protected:
void SetupLinkWebBundleElementAndImgElement(const GURL& bundle_url,
const GURL subresource_url) {
// Navigate to the test page.
ASSERT_TRUE(NavigateToURL(shell(), GURL("http://foo.com/title1.html")));
const char kScriptTemplate[] = R"(
const link = document.createElement('link');
link.rel = 'webbundle';
link.href = $1;
link.resources.add($2);
document.body.appendChild(link);
const img = document.createElement('img');
img.src = $2;
document.body.appendChild(img);
)";
// Insert a <link> element for webbundle subresoruce loading, and insert an
// <img> element which loads a resource from the webbundle.
ASSERT_TRUE(ExecJs(
shell(), JsReplace(kScriptTemplate, bundle_url, subresource_url)));
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// CrossSiteDocumentBlockingWebBundleTest has 4 tests; a cartesian product of
// 1) cross-origin bundle, 2) same-origin bundle
// X
// A). CORB-protected MIME type (e.g. text/json), B) other type (e.g. image/png)
IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
CrossOriginWebBundleSubresoruceJson) {
embedded_test_server()->StartAcceptingConnections();
GURL bundle_url("http://cross-origin.com/web_bundle/cross_origin.wbn");
GURL subresource_url("http://cross-origin.com/web_bundle/resource.json");
RequestInterceptor interceptor(subresource_url);
SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
interceptor.WaitForRequestCompletion();
EXPECT_EQ(0, interceptor.completion_status().error_code);
EXPECT_EQ("", interceptor.response_body())
<< "JSON in a cross-origin webbundle should be blocked by CORB";
}
IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
CrossOriginWebBundleSubresorucePng) {
embedded_test_server()->StartAcceptingConnections();
GURL bundle_url("http://cross-origin.com/web_bundle/cross_origin.wbn");
GURL subresource_url("http://cross-origin.com/web_bundle/resource.png");
RequestInterceptor interceptor(subresource_url);
SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
interceptor.WaitForRequestCompletion();
EXPECT_EQ(0, interceptor.completion_status().error_code);
EXPECT_EQ("broken png", interceptor.response_body())
<< "PNG in a cross-origin webbundle should not be blocked";
}
IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
SameOriginWebBundleSubresoruceJson) {
embedded_test_server()->StartAcceptingConnections();
GURL bundle_url("http://foo.com/web_bundle/same_origin.wbn");
GURL subresource_url("http://foo.com/web_bundle/resource.json");
RequestInterceptor interceptor(subresource_url);
SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
interceptor.WaitForRequestCompletion();
EXPECT_EQ(0, interceptor.completion_status().error_code);
EXPECT_EQ("{ secret: 1 }", interceptor.response_body())
<< "JSON in a same-origin webbundle should not be blocked";
}
IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
SameOriginWebBundleSubresorucePng) {
embedded_test_server()->StartAcceptingConnections();
GURL bundle_url("http://foo.com/web_bundle/same_origin.wbn");
GURL subresource_url("http://foo.com/web_bundle/resource.png");
RequestInterceptor interceptor(subresource_url);
SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
interceptor.WaitForRequestCompletion();
EXPECT_EQ(0, interceptor.completion_status().error_code);
EXPECT_EQ("broken png", interceptor.response_body())
<< "PNG in a same-origin webbundle should not be blocked";
}
} // namespace content } // namespace content
{
"log": {
"entries": [
{
"request": {
"method": "GET",
"url": "http://cross-origin.com/web_bundle/resource.json",
"headers": []
},
"response": {
"status": 200,
"headers": [
{
"name": "Content-type",
"value": "application/json"
}
],
"content": {
"text": "{ secret: 1 }"
}
}
},
{
"request": {
"method": "GET",
"url": "http://cross-origin.com/web_bundle/resource.png",
"headers": []
},
"response": {
"status": 200,
"headers": [
{
"name": "Content-type",
"value": "image/png"
}
],
"content": {
"text": "broken png"
}
}
}
]
}
}
HTTP/1.1 200 OK
Content-Type: application/webbundle
Access-Control-Allow-Origin: *
...@@ -52,3 +52,17 @@ gen-bundle \ ...@@ -52,3 +52,17 @@ gen-bundle \
-primaryURL https://test.example.org/ \ -primaryURL https://test.example.org/ \
-har variants_test.har \ -har variants_test.har \
-o variants_test.wbn -o variants_test.wbn
# Generate a WBN which will be used as a cross origin bundle.
gen-bundle \
-version b1 \
-har cross_origin.har \
-primaryURL http://cross-origin.com/web_bundle/resource.json \
-o cross_origin.wbn
# Generate a WBN which will be used as a same origin bundle.
gen-bundle \
-version b1 \
-har same_origin.har \
-primaryURL http://foo.com/web_bundle/resource.json \
-o same_origin.wbn
{
"log": {
"entries": [
{
"request": {
"method": "GET",
"url": "http://foo.com/web_bundle/resource.json",
"headers": []
},
"response": {
"status": 200,
"headers": [
{
"name": "Content-type",
"value": "application/json"
}
],
"content": {
"text": "{ secret: 1 }"
}
}
},
{
"request": {
"method": "GET",
"url": "http://foo.com/web_bundle/resource.png",
"headers": []
},
"response": {
"status": 200,
"headers": [
{
"name": "Content-type",
"value": "image/png"
}
],
"content": {
"text": "broken png"
}
}
}
]
}
}
...@@ -278,7 +278,7 @@ void URLLoaderFactory::CreateLoaderAndStart( ...@@ -278,7 +278,7 @@ void URLLoaderFactory::CreateLoaderAndStart(
DCHECK(url_request.web_bundle_token_params.has_value()); DCHECK(url_request.web_bundle_token_params.has_value());
base::WeakPtr<WebBundleURLLoaderFactory> web_bundle_url_loader_factory = base::WeakPtr<WebBundleURLLoaderFactory> web_bundle_url_loader_factory =
context_->GetWebBundleManager().CreateWebBundleURLLoaderFactory( context_->GetWebBundleManager().CreateWebBundleURLLoaderFactory(
url_request.url, *url_request.web_bundle_token_params); url_request.url, *url_request.web_bundle_token_params, params_);
client = client =
web_bundle_url_loader_factory->WrapURLLoaderClient(std::move(client)); web_bundle_url_loader_factory->WrapURLLoaderClient(std::move(client));
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "services/network/network_context.h"
#include "services/network/public/mojom/web_bundle_handle.mojom.h" #include "services/network/public/mojom/web_bundle_handle.mojom.h"
#include "services/network/web_bundle_url_loader_factory.h" #include "services/network/web_bundle_url_loader_factory.h"
...@@ -18,7 +19,8 @@ WebBundleManager::~WebBundleManager() = default; ...@@ -18,7 +19,8 @@ WebBundleManager::~WebBundleManager() = default;
base::WeakPtr<WebBundleURLLoaderFactory> base::WeakPtr<WebBundleURLLoaderFactory>
WebBundleManager::CreateWebBundleURLLoaderFactory( WebBundleManager::CreateWebBundleURLLoaderFactory(
const GURL& bundle_url, const GURL& bundle_url,
const ResourceRequest::WebBundleTokenParams& web_bundle_token_params) { const ResourceRequest::WebBundleTokenParams& web_bundle_token_params,
const mojom::URLLoaderFactoryParamsPtr& factory_params) {
DCHECK(factories_.find(web_bundle_token_params.token) == factories_.end()); DCHECK(factories_.find(web_bundle_token_params.token) == factories_.end());
mojo::Remote<mojom::WebBundleHandle> remote( mojo::Remote<mojom::WebBundleHandle> remote(
...@@ -32,8 +34,9 @@ WebBundleManager::CreateWebBundleURLLoaderFactory( ...@@ -32,8 +34,9 @@ WebBundleManager::CreateWebBundleURLLoaderFactory(
// |this| outlives |remote|. // |this| outlives |remote|.
base::Unretained(this), web_bundle_token_params.token)); base::Unretained(this), web_bundle_token_params.token));
auto factory = std::make_unique<WebBundleURLLoaderFactory>(bundle_url, auto factory = std::make_unique<WebBundleURLLoaderFactory>(
std::move(remote)); bundle_url, std::move(remote),
factory_params->request_initiator_origin_lock);
auto weak_factory = factory->GetWeakPtr(); auto weak_factory = factory->GetWeakPtr();
factories_.insert({web_bundle_token_params.token, std::move(factory)}); factories_.insert({web_bundle_token_params.token, std::move(factory)});
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom-forward.h"
namespace network { namespace network {
...@@ -28,7 +29,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleManager { ...@@ -28,7 +29,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleManager {
base::WeakPtr<WebBundleURLLoaderFactory> CreateWebBundleURLLoaderFactory( base::WeakPtr<WebBundleURLLoaderFactory> CreateWebBundleURLLoaderFactory(
const GURL& bundle_url, const GURL& bundle_url,
const ResourceRequest::WebBundleTokenParams& params); const ResourceRequest::WebBundleTokenParams& params,
const mojom::URLLoaderFactoryParamsPtr& factory_params);
base::WeakPtr<WebBundleURLLoaderFactory> GetWebBundleURLLoaderFactory( base::WeakPtr<WebBundleURLLoaderFactory> GetWebBundleURLLoaderFactory(
const base::UnguessableToken& token); const base::UnguessableToken& token);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/web_bundle_handle.mojom.h" #include "services/network/public/mojom/web_bundle_handle.mojom.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -37,8 +38,8 @@ TEST_F(WebBundleManagerTest, RemoveFactoryWhenDisconnected) { ...@@ -37,8 +38,8 @@ TEST_F(WebBundleManagerTest, RemoveFactoryWhenDisconnected) {
auto token_params = auto token_params =
ResourceRequest::WebBundleTokenParams(token, std::move(handle)); ResourceRequest::WebBundleTokenParams(token, std::move(handle));
auto factory = auto factory = manager.CreateWebBundleURLLoaderFactory(
manager.CreateWebBundleURLLoaderFactory(GURL(kBundleUrl), token_params); GURL(kBundleUrl), token_params, mojom::URLLoaderFactoryParams::New());
ASSERT_TRUE(factory); ASSERT_TRUE(factory);
ASSERT_TRUE(manager.GetWebBundleURLLoaderFactory(token)); ASSERT_TRUE(manager.GetWebBundleURLLoaderFactory(token));
// Getting out of scope to delete |receiver|. // Getting out of scope to delete |receiver|.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "services/network/web_bundle_url_loader_factory.h" #include "services/network/web_bundle_url_loader_factory.h"
#include "base/optional.h"
#include "components/web_package/web_bundle_parser.h" #include "components/web_package/web_bundle_parser.h"
#include "components/web_package/web_bundle_utils.h" #include "components/web_package/web_bundle_utils.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h"
...@@ -11,6 +12,7 @@ ...@@ -11,6 +12,7 @@
#include "mojo/public/cpp/system/data_pipe_drainer.h" #include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "mojo/public/cpp/system/data_pipe_producer.h" #include "mojo/public/cpp/system/data_pipe_producer.h"
#include "net/http/http_status_code.h" #include "net/http/http_status_code.h"
#include "services/network/public/cpp/cross_origin_read_blocking.h"
namespace network { namespace network {
...@@ -128,8 +130,12 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader { ...@@ -128,8 +130,12 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader {
public: public:
URLLoader(mojo::PendingReceiver<mojom::URLLoader> loader, URLLoader(mojo::PendingReceiver<mojom::URLLoader> loader,
const ResourceRequest& request, const ResourceRequest& request,
mojo::PendingRemote<mojom::URLLoaderClient> client) mojo::PendingRemote<mojom::URLLoaderClient> client,
const base::Optional<url::Origin>& request_initiator_origin_lock)
: url_(request.url), : url_(request.url),
request_mode_(request.mode),
request_initiator_(request.request_initiator),
request_initiator_origin_lock_(request_initiator_origin_lock),
receiver_(this, std::move(loader)), receiver_(this, std::move(loader)),
client_(std::move(client)) { client_(std::move(client)) {
receiver_.set_disconnect_handler( receiver_.set_disconnect_handler(
...@@ -139,6 +145,15 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader { ...@@ -139,6 +145,15 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader {
URLLoader& operator=(const URLLoader&) = delete; URLLoader& operator=(const URLLoader&) = delete;
const GURL& url() const { return url_; } const GURL& url() const { return url_; }
const mojom::RequestMode& request_mode() const { return request_mode_; }
const base::Optional<url::Origin>& request_initiator() const {
return request_initiator_;
}
const base::Optional<url::Origin>& request_initiator_origin_lock() const {
return request_initiator_origin_lock_;
}
base::WeakPtr<URLLoader> GetWeakPtr() { base::WeakPtr<URLLoader> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr(); return weak_ptr_factory_.GetWeakPtr();
...@@ -164,6 +179,39 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader { ...@@ -164,6 +179,39 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader {
delete this; delete this;
} }
void BlockResponseForCorb(mojom::URLResponseHeadPtr response_head) {
// A minimum implementation to block CORB-protected resources.
//
// TODO(crbug.com/1082020): Re-use
// network::URLLoader::BlockResponseForCorb(), instead of copying
// essential parts from there, so that the two implementations won't
// diverge further. That requires non-trivial refactoring.
CrossOriginReadBlocking::SanitizeBlockedResponse(response_head.get());
client_->OnReceiveResponse(std::move(response_head));
// Send empty body to the URLLoaderClient.
mojo::ScopedDataPipeProducerHandle producer;
mojo::ScopedDataPipeConsumerHandle consumer;
if (CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) {
OnFail(net::ERR_INSUFFICIENT_RESOURCES);
return;
}
producer.reset();
client_->OnStartLoadingResponseBody(std::move(consumer));
URLLoaderCompletionStatus status;
status.error_code = net::OK;
status.completion_time = base::TimeTicks::Now();
status.encoded_data_length = 0;
status.encoded_body_length = 0;
status.decoded_body_length = 0;
client_->OnComplete(status);
// Reset the connection to the URLLoaderClient. This helps ensure that we
// won't accidentally leak any data to the renderer from this point on.
client_.reset();
}
private: private:
// mojom::URLLoader // mojom::URLLoader
void FollowRedirect( void FollowRedirect(
...@@ -185,6 +233,15 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader { ...@@ -185,6 +233,15 @@ class WebBundleURLLoaderFactory::URLLoader : public mojom::URLLoader {
void OnMojoDisconnect() { delete this; } void OnMojoDisconnect() { delete this; }
const GURL url_; const GURL url_;
mojom::RequestMode request_mode_;
base::Optional<url::Origin> request_initiator_;
// It is safe to hold |request_initiator_origin_lock_| in this factory because
// 1). |request_initiator_origin_lock| is a property of |URLLoaderFactory|
// (or, more accurately a property of |URLLoaderFactoryParams|), and
// 2) |WebURLLoader| is always associated with the same URLLoaderFactory
// (via URLLoaderFactory -> WebBundleManager -> WebBundleURLLoaderFactory
// -> WebBundleURLLoader).
const base::Optional<url::Origin> request_initiator_origin_lock_;
mojo::Receiver<mojom::URLLoader> receiver_; mojo::Receiver<mojom::URLLoader> receiver_;
mojo::Remote<mojom::URLLoaderClient> client_; mojo::Remote<mojom::URLLoaderClient> client_;
base::WeakPtrFactory<URLLoader> weak_ptr_factory_{this}; base::WeakPtrFactory<URLLoader> weak_ptr_factory_{this};
...@@ -317,9 +374,11 @@ class WebBundleURLLoaderFactory::BundleDataSource ...@@ -317,9 +374,11 @@ class WebBundleURLLoaderFactory::BundleDataSource
WebBundleURLLoaderFactory::WebBundleURLLoaderFactory( WebBundleURLLoaderFactory::WebBundleURLLoaderFactory(
const GURL& bundle_url, const GURL& bundle_url,
mojo::Remote<mojom::WebBundleHandle> web_bundle_handle) mojo::Remote<mojom::WebBundleHandle> web_bundle_handle,
const base::Optional<url::Origin>& request_initiator_origin_lock)
: bundle_url_(bundle_url), : bundle_url_(bundle_url),
web_bundle_handle_(std::move(web_bundle_handle)) {} web_bundle_handle_(std::move(web_bundle_handle)),
request_initiator_origin_lock_(request_initiator_origin_lock) {}
WebBundleURLLoaderFactory::~WebBundleURLLoaderFactory() { WebBundleURLLoaderFactory::~WebBundleURLLoaderFactory() {
for (auto loader : pending_loaders_) { for (auto loader : pending_loaders_) {
...@@ -368,7 +427,8 @@ void WebBundleURLLoaderFactory::CreateLoaderAndStart( ...@@ -368,7 +427,8 @@ void WebBundleURLLoaderFactory::CreateLoaderAndStart(
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
TRACE_EVENT0("loading", "WebBundleURLLoaderFactory::CreateLoaderAndStart"); TRACE_EVENT0("loading", "WebBundleURLLoaderFactory::CreateLoaderAndStart");
URLLoader* loader = URLLoader* loader =
new URLLoader(std::move(receiver), url_request, std::move(client)); new URLLoader(std::move(receiver), url_request, std::move(client),
request_initiator_origin_lock_);
if (metadata_error_) { if (metadata_error_) {
loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); loader->OnFail(net::ERR_INVALID_WEB_BUNDLE);
return; return;
...@@ -459,6 +519,21 @@ void WebBundleURLLoaderFactory::OnResponseParsed( ...@@ -459,6 +519,21 @@ void WebBundleURLLoaderFactory::OnResponseParsed(
mojom::URLResponseHeadPtr response_head = mojom::URLResponseHeadPtr response_head =
web_package::CreateResourceResponse(response); web_package::CreateResourceResponse(response);
response_head->web_bundle_url = bundle_url_; response_head->web_bundle_url = bundle_url_;
// Add an artifical "X-Content-Type-Options: "nosniff" header, which is
// explained at
// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#name-responses.
response_head->headers->SetHeader("X-Content-Type-Options", "nosniff");
auto corb_analyzer =
std::make_unique<CrossOriginReadBlocking::ResponseAnalyzer>(
loader->url(), loader->request_initiator(), *response_head,
loader->request_initiator_origin_lock(), loader->request_mode());
if (corb_analyzer->ShouldBlock()) {
loader->BlockResponseForCorb(std::move(response_head));
return;
}
loader->OnResponse(std::move(response_head)); loader->OnResponse(std::move(response_head));
mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeProducerHandle producer;
......
...@@ -20,7 +20,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleURLLoaderFactory ...@@ -20,7 +20,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleURLLoaderFactory
public: public:
WebBundleURLLoaderFactory( WebBundleURLLoaderFactory(
const GURL& bundle_url, const GURL& bundle_url,
mojo::Remote<mojom::WebBundleHandle> web_bundle_handle); mojo::Remote<mojom::WebBundleHandle> web_bundle_handle,
const base::Optional<url::Origin>& request_initiator_origin_lock);
~WebBundleURLLoaderFactory() override; ~WebBundleURLLoaderFactory() override;
WebBundleURLLoaderFactory(const WebBundleURLLoaderFactory&) = delete; WebBundleURLLoaderFactory(const WebBundleURLLoaderFactory&) = delete;
WebBundleURLLoaderFactory& operator=(const WebBundleURLLoaderFactory&) = WebBundleURLLoaderFactory& operator=(const WebBundleURLLoaderFactory&) =
...@@ -57,6 +58,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleURLLoaderFactory ...@@ -57,6 +58,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebBundleURLLoaderFactory
GURL bundle_url_; GURL bundle_url_;
mojo::Remote<mojom::WebBundleHandle> web_bundle_handle_; mojo::Remote<mojom::WebBundleHandle> web_bundle_handle_;
const base::Optional<::url::Origin> request_initiator_origin_lock_;
std::unique_ptr<BundleDataSource> source_; std::unique_ptr<BundleDataSource> source_;
mojo::Remote<web_package::mojom::WebBundleParser> parser_; mojo::Remote<web_package::mojom::WebBundleParser> parser_;
web_package::mojom::BundleMetadataPtr metadata_; web_package::mojom::BundleMetadataPtr metadata_;
......
...@@ -18,11 +18,16 @@ namespace network { ...@@ -18,11 +18,16 @@ namespace network {
namespace { namespace {
const char kInitiatorUrl[] = "https://example.com/";
const char kBundleUrl[] = "https://example.com/bundle.wbn"; const char kBundleUrl[] = "https://example.com/bundle.wbn";
const char kResourceUrl[] = "https://example.com/"; const char kResourceUrl[] = "https://example.com/";
const char kResourceUrl2[] = "https://example.com/another"; const char kResourceUrl2[] = "https://example.com/another";
const char kResourceUrl3[] = "https://example.com/yetanother"; const char kResourceUrl3[] = "https://example.com/yetanother";
// Cross origin resources
const char kCrossOriginJsonUrl[] = "https://other.com/resource.json";
const char kCrossOriginJsUrl[] = "https://other.com/resource.js";
std::vector<uint8_t> CreateSmallBundle() { std::vector<uint8_t> CreateSmallBundle() {
web_package::test::WebBundleBuilder builder(kResourceUrl, web_package::test::WebBundleBuilder builder(kResourceUrl,
"" /* manifest_url */); "" /* manifest_url */);
...@@ -47,6 +52,19 @@ std::vector<uint8_t> CreateLargeBundle() { ...@@ -47,6 +52,19 @@ std::vector<uint8_t> CreateLargeBundle() {
return builder.CreateBundle(); return builder.CreateBundle();
} }
std::vector<uint8_t> CreateCrossOriginBundle() {
web_package::test::WebBundleBuilder builder(kCrossOriginJsonUrl,
"" /* manifest_url */);
builder.AddExchange(
kCrossOriginJsonUrl,
{{":status", "200"}, {"content-type", "application/json"}},
"{ secret: 1 }");
builder.AddExchange(kCrossOriginJsUrl,
{{":status", "200"}, {"content-type", "application/js"}},
"const not_secret = 1;");
return builder.CreateBundle();
}
class TestWebBundleHandle : public mojom::WebBundleHandle { class TestWebBundleHandle : public mojom::WebBundleHandle {
public: public:
explicit TestWebBundleHandle( explicit TestWebBundleHandle(
...@@ -96,8 +114,8 @@ class WebBundleURLLoaderFactoryTest : public ::testing::Test { ...@@ -96,8 +114,8 @@ class WebBundleURLLoaderFactoryTest : public ::testing::Test {
mojo::Remote<mojom::WebBundleHandle> handle; mojo::Remote<mojom::WebBundleHandle> handle;
handle_ = std::make_unique<TestWebBundleHandle>( handle_ = std::make_unique<TestWebBundleHandle>(
handle.BindNewPipeAndPassReceiver()); handle.BindNewPipeAndPassReceiver());
factory_ = std::make_unique<WebBundleURLLoaderFactory>(GURL(kBundleUrl), factory_ = std::make_unique<WebBundleURLLoaderFactory>(
std::move(handle)); GURL(kBundleUrl), std::move(handle), base::nullopt);
factory_->SetBundleStream(std::move(consumer)); factory_->SetBundleStream(std::move(consumer));
} }
...@@ -118,6 +136,8 @@ class WebBundleURLLoaderFactoryTest : public ::testing::Test { ...@@ -118,6 +136,8 @@ class WebBundleURLLoaderFactoryTest : public ::testing::Test {
network::ResourceRequest request; network::ResourceRequest request;
request.url = url; request.url = url;
request.method = "GET"; request.method = "GET";
request.request_initiator = url::Origin::Create(GURL(kInitiatorUrl));
StartRequestResult result; StartRequestResult result;
result.client = std::make_unique<network::TestURLLoaderClient>(); result.client = std::make_unique<network::TestURLLoaderClient>();
factory_->CreateLoaderAndStart( factory_->CreateLoaderAndStart(
...@@ -340,4 +360,36 @@ TEST_F(WebBundleURLLoaderFactoryTest, TruncatedBundle) { ...@@ -340,4 +360,36 @@ TEST_F(WebBundleURLLoaderFactoryTest, TruncatedBundle) {
EXPECT_EQ(last_bundle_error()->second, "Error reading response header."); EXPECT_EQ(last_bundle_error()->second, "Error reading response header.");
} }
TEST_F(WebBundleURLLoaderFactoryTest, CrossOiginJson) {
WriteBundle(CreateCrossOriginBundle());
FinishWritingBundle();
auto request = StartRequest(GURL(kCrossOriginJsonUrl));
request.client->RunUntilComplete();
EXPECT_EQ(net::OK, request.client->completion_status().error_code);
EXPECT_FALSE(last_bundle_error().has_value());
std::string body;
ASSERT_TRUE(mojo::BlockingCopyToString(
request.client->response_body_release(), &body));
EXPECT_TRUE(body.empty())
<< "body should be empty because JSON is a CORB-protected resource";
}
TEST_F(WebBundleURLLoaderFactoryTest, CrossOriginJs) {
WriteBundle(CreateCrossOriginBundle());
FinishWritingBundle();
auto request = StartRequest(GURL(kCrossOriginJsUrl));
request.client->RunUntilComplete();
EXPECT_EQ(net::OK, request.client->completion_status().error_code);
EXPECT_FALSE(last_bundle_error().has_value());
std::string body;
ASSERT_TRUE(mojo::BlockingCopyToString(
request.client->response_body_release(), &body));
EXPECT_EQ("const not_secret = 1;", body)
<< "body should be valid one because JS is not a CORB protected resource";
}
} // namespace network } // namespace network
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment