Commit ed3c2dd1 authored by Makoto Shimazu's avatar Makoto Shimazu Committed by Commit Bot

Plumb COEP reporter for requests initiated from a service worker

This adds COEP reporter for service worker contexts. CORP failures
happening on subresource requests initiated from a service worker are
reported to the COEP reporter owned by EmbeddedWorkerInstance.

Bug: 1056122
Change-Id: I7884487e0261b4d85bf98b5844c3e34c0b985ebe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2098166
Commit-Queue: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749584}
parent 50e57584
......@@ -73,6 +73,8 @@ ServiceWorkerDevToolsAgentHost::ServiceWorkerDevToolsAgentHost(
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
const base::UnguessableToken& devtools_worker_token)
: DevToolsAgentHostImpl(devtools_worker_token.ToString()),
state_(WORKER_NOT_READY),
......@@ -86,7 +88,8 @@ ServiceWorkerDevToolsAgentHost::ServiceWorkerDevToolsAgentHost(
scope_(scope),
version_installed_time_(is_installed_version ? base::Time::Now()
: base::Time()),
cross_origin_embedder_policy_(std::move(cross_origin_embedder_policy)) {
cross_origin_embedder_policy_(std::move(cross_origin_embedder_policy)),
coep_reporter_(std::move(coep_reporter)) {
NotifyCreated();
}
......@@ -177,8 +180,11 @@ void ServiceWorkerDevToolsAgentHost::WorkerReadyForInspection(
}
void ServiceWorkerDevToolsAgentHost::UpdateCrossOriginEmbedderPolicy(
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter) {
cross_origin_embedder_policy_ = std::move(cross_origin_embedder_policy);
coep_reporter_.Bind(std::move(coep_reporter));
}
void ServiceWorkerDevToolsAgentHost::WorkerRestarted(int worker_process_id,
......@@ -215,6 +221,16 @@ void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories(
}
const url::Origin origin = url::Origin::Create(url_);
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_script_loader;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_subresource_loader;
if (coep_reporter_) {
coep_reporter_->Clone(
coep_reporter_for_script_loader.InitWithNewPipeAndPassReceiver());
coep_reporter_->Clone(
coep_reporter_for_subresource_loader.InitWithNewPipeAndPassReceiver());
}
// Use the default CrossOriginEmbedderPolicy if
// |cross_origin_embedder_policy_| is nullopt. It's acceptable because the
// factory bundles are updated with correct COEP value before any subresource
......@@ -223,11 +239,13 @@ void ServiceWorkerDevToolsAgentHost::UpdateLoaderFactories(
rph, worker_route_id_, origin,
cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
: network::CrossOriginEmbedderPolicy(),
std::move(coep_reporter_for_script_loader),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, worker_route_id_, origin,
cross_origin_embedder_policy_ ? cross_origin_embedder_policy_.value()
: network::CrossOriginEmbedderPolicy(),
std::move(coep_reporter_for_subresource_loader),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) {
......
......@@ -15,6 +15,7 @@
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/service_worker_devtools_manager.h"
#include "services/network/public/cpp/cross_origin_embedder_policy.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
namespace content {
......@@ -38,6 +39,8 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
const base::UnguessableToken& devtools_worker_token);
// DevToolsAgentHost overrides.
......@@ -54,7 +57,9 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote,
mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver);
void UpdateCrossOriginEmbedderPolicy(
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter);
void WorkerDestroyed();
void WorkerVersionInstalled();
void WorkerVersionDoomed();
......@@ -104,6 +109,8 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl {
base::Time version_doomed_time_;
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy_;
mojo::Remote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDevToolsAgentHost);
};
......
......@@ -54,6 +54,8 @@ void ServiceWorkerDevToolsManager::WorkerCreated(
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
base::UnguessableToken* devtools_worker_token,
bool* pause_on_start) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
......@@ -71,7 +73,8 @@ void ServiceWorkerDevToolsManager::WorkerCreated(
new ServiceWorkerDevToolsAgentHost(
worker_process_id, worker_route_id, context, context_weak,
version_id, url, scope, is_installed_version,
cross_origin_embedder_policy, *devtools_worker_token);
cross_origin_embedder_policy, std::move(coep_reporter),
*devtools_worker_token);
live_hosts_[worker_id] = host;
*pause_on_start = debug_service_worker_on_start_;
for (auto& observer : observer_list_) {
......@@ -112,15 +115,17 @@ void ServiceWorkerDevToolsManager::WorkerReadyForInspection(
void ServiceWorkerDevToolsManager::UpdateCrossOriginEmbedderPolicy(
int worker_process_id,
int worker_route_id,
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy) {
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const WorkerId worker_id(worker_process_id, worker_route_id);
auto it = live_hosts_.find(worker_id);
if (it == live_hosts_.end())
return;
scoped_refptr<ServiceWorkerDevToolsAgentHost> host = it->second;
host->UpdateCrossOriginEmbedderPolicy(
std::move(cross_origin_embedder_policy));
host->UpdateCrossOriginEmbedderPolicy(std::move(cross_origin_embedder_policy),
std::move(coep_reporter));
}
void ServiceWorkerDevToolsManager::WorkerVersionInstalled(int worker_process_id,
......
......@@ -17,6 +17,7 @@
#include "base/unguessable_token.h"
#include "content/common/content_export.h"
#include "services/network/public/cpp/cross_origin_embedder_policy.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
#include "url/gurl.h"
......@@ -59,18 +60,21 @@ class CONTENT_EXPORT ServiceWorkerDevToolsManager {
BrowserContext* browser_context,
std::vector<scoped_refptr<ServiceWorkerDevToolsAgentHost>>* result);
void WorkerCreated(int worker_process_id,
int worker_route_id,
const ServiceWorkerContextCore* context,
base::WeakPtr<ServiceWorkerContextCore> context_weak,
int64_t version_id,
const GURL& url,
const GURL& scope,
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
base::UnguessableToken* devtools_worker_token,
bool* pause_on_start);
void WorkerCreated(
int worker_process_id,
int worker_route_id,
const ServiceWorkerContextCore* context,
base::WeakPtr<ServiceWorkerContextCore> context_weak,
int64_t version_id,
const GURL& url,
const GURL& scope,
bool is_installed_version,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
base::UnguessableToken* devtools_worker_token,
bool* pause_on_start);
void WorkerReadyForInspection(
int worker_process_id,
int worker_route_id,
......@@ -79,7 +83,9 @@ class CONTENT_EXPORT ServiceWorkerDevToolsManager {
void UpdateCrossOriginEmbedderPolicy(
int worker_process_id,
int worker_route_id,
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy);
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter);
void WorkerVersionInstalled(int worker_process_id, int worker_route_id);
void WorkerVersionDoomed(int worker_process_id, int worker_route_id);
void WorkerDestroyed(int worker_process_id, int worker_route_id);
......
......@@ -41,6 +41,7 @@
namespace content {
class CrossOriginEmbedderPolicyReporter;
class RenderProcessHost;
class ServiceWorkerContentSettingsProxyImpl;
class ServiceWorkerContextCore;
......@@ -235,8 +236,17 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
const url::Origin& origin,
const base::Optional<network::CrossOriginEmbedderPolicy>&
cross_origin_embedder_policy,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
ContentBrowserClient::URLLoaderFactoryType factory_type);
// Creates a set of factory bundles for scripts and subresources. This must be
// called after the COEP value for the worker script is known.
using CreateFactoryBundlesCallback = base::OnceCallback<void(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresouce_bundle)>;
void CreateFactoryBundles(CreateFactoryBundlesCallback callback);
private:
typedef base::ObserverList<Listener>::Unchecked ListenerList;
class ScopedLifetimeTracker;
......@@ -311,6 +321,13 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
void BindCacheStorageInternal();
void OnCreatedFactoryBundles(
CreateFactoryBundlesCallback callback,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresouce_bundle,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter);
base::WeakPtr<ServiceWorkerContextCore> context_;
ServiceWorkerVersion* owner_version_;
......@@ -374,7 +391,11 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
std::vector<mojo::PendingReceiver<blink::mojom::CacheStorage>>
pending_cache_storage_receivers_;
std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
// COEP Reporter connected to the URLLoaderFactories that handles subresource
// requests initiated from the service worker. The impl lives on the UI
// thread, and |coep_reporter_| has the ownership of the impl instance.
mojo::Remote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_;
base::WeakPtrFactory<EmbeddedWorkerInstance> weak_factory_{this};
......
......@@ -205,36 +205,6 @@ base::TimeDelta GetUpdateDelay() {
return base::TimeDelta::FromMilliseconds(kUpdateDelayParam.Get());
}
using CreateFactoryBundleForSubresourceOnUICallback = base::OnceCallback<void(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresouce_bundle)>;
void CreateFactoryBundleForSubresourceOnUI(
int process_id,
int routing_id,
const url::Origin& origin,
base::Optional<network::CrossOriginEmbedderPolicy>
cross_origin_embedder_policy,
CreateFactoryBundleForSubresourceOnUICallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* rph = RenderProcessHost::FromID(process_id);
if (!rph) {
// Return nullptr because we can't create a factory bundle because of
// missing renderer.
ServiceWorkerContextWrapper::RunOrPostTaskOnCoreThread(
FROM_HERE, base::BindOnce(std::move(callback), nullptr, nullptr));
return;
}
auto script_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin, cross_origin_embedder_policy,
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
auto subresource_bundle = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin, cross_origin_embedder_policy,
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
ServiceWorkerContextWrapper::RunOrPostTaskOnCoreThread(
FROM_HERE, base::BindOnce(std::move(callback), std::move(script_bundle),
std::move(subresource_bundle)));
}
} // namespace
constexpr base::TimeDelta ServiceWorkerVersion::kTimeoutTimerDelay;
......@@ -997,15 +967,9 @@ void ServiceWorkerVersion::OnMainScriptLoaded() {
// TODO(https://crbug.com/1039613): Update the loader factories passed to the
// script loader factory too.
DCHECK_EQ(NEW, status());
RunOrPostTaskOnThread(
FROM_HERE, BrowserThread::UI,
base::BindOnce(
&CreateFactoryBundleForSubresourceOnUI,
embedded_worker()->process_id(),
embedded_worker()->worker_devtools_agent_route_id(), script_origin(),
cross_origin_embedder_policy(),
base::BindOnce(&ServiceWorkerVersion::InitializeGlobalScope,
weak_factory_.GetWeakPtr())));
embedded_worker_->CreateFactoryBundles(
base::BindOnce(&ServiceWorkerVersion::InitializeGlobalScope,
weak_factory_.GetWeakPtr()));
}
void ServiceWorkerVersion::InitializeGlobalScope(
......
......@@ -169,7 +169,9 @@ network::mojom::URLLoaderFactoryParamsPtr
URLLoaderFactoryParamsHelper::CreateForWorker(
RenderProcessHost* process,
const url::Origin& request_initiator,
const net::NetworkIsolationKey& network_isolation_key) {
const net::NetworkIsolationKey& network_isolation_key,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter) {
return CreateParams(process,
request_initiator, // origin
request_initiator, // request_initiator_site_lock
......@@ -177,8 +179,8 @@ URLLoaderFactoryParamsHelper::CreateForWorker(
false, // is_trusted
base::nullopt, // top_frame_token
network_isolation_key,
nullptr, // client_security_state
mojo::NullRemote(), // coep_reporter
nullptr, // client_security_state
std::move(coep_reporter),
false, // allow_universal_access_from_file_urls
false); // is_for_isolated_world
}
......
......@@ -66,7 +66,9 @@ class URLLoaderFactoryParamsHelper {
static network::mojom::URLLoaderFactoryParamsPtr CreateForWorker(
RenderProcessHost* process,
const url::Origin& request_initiator,
const net::NetworkIsolationKey& network_isolation_key);
const net::NetworkIsolationKey& network_isolation_key,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter);
// TODO(kinuko, lukasza): https://crbug.com/891872: Remove, once all
// URLLoaderFactories vended to a renderer process are associated with a
......
......@@ -263,10 +263,13 @@ SharedWorkerHost::CreateNetworkFactoryForSubresources(
pending_default_factory.InitWithNewPipeAndPassReceiver();
const url::Origin& origin = instance_.constructor_origin();
// TODO(https://crbug.com/1060832): Implement COEP reporter for shared
// workers.
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker(
worker_process_host_, origin,
net::NetworkIsolationKey(origin, origin));
net::NetworkIsolationKey(origin, origin),
/*coep_reporter=*/mojo::NullRemote());
GetContentClient()->browser()->WillCreateURLLoaderFactory(
worker_process_host_->GetBrowserContext(),
/*frame=*/nullptr, worker_process_host_->GetID(),
......
......@@ -320,9 +320,12 @@ void WorkerScriptFetchInitiator::CreateScriptLoader(
DCHECK(factory_bundle_for_browser_info);
const url::Origin& request_initiator = *resource_request->request_initiator;
// TODO(https://crbug.com/1060837): Pass the Mojo remote which is connected
// to the COEP reporter in DedicatedWorkerHost.
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForWorker(
factory_process, request_initiator, trusted_network_isolation_key);
factory_process, request_initiator, trusted_network_isolation_key,
/*coep_reporter=*/mojo::NullRemote());
mojo::PendingReceiver<network::mojom::URLLoaderFactory>
default_factory_receiver =
......
......@@ -118,6 +118,68 @@ async_test(async (t) => {
}
}, 'subresource CORP');
async_test(async (t) => {
try {
const IFRAME_SRC = 'resources/coep-iframe.html';
const SCOPE = new URL(IFRAME_SRC, location).pathname;
const SCRIPT =
'resources/sw.js?' +
`pipe=header(service-worker-allowed,${SCOPE})|` +
'header(cross-origin-embedder-policy,require-corp%3breport-to=%22endpoint%22)|' +
'header(cross-origin-embedder-policy-report-only,require-corp%3breport-to=%22report-only-endpoint%22)';
const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
await wait_for_state(t, reg.installing, 'activated');
const iframe = document.createElement('iframe');
t.add_cleanup(() => {
iframe.remove();
reg.unregister();
});
iframe.src = IFRAME_SRC;
document.body.appendChild(iframe);
await new Promise(resolve => {
iframe.addEventListener('load', resolve, {once: true});
});
document.body.appendChild(iframe);
await new Promise(resolve => {
iframe.addEventListener('load', resolve, {once: true});
});
function fetchInIframe(url) {
const init = { mode: 'no-cors', cache: 'no-store' };
iframe.contentWindow.fetch(url, init).catch(() => {});
}
const suffix = 'subresource-corp-sw';
const sameOriginUrl = `/common/text-plain.txt?${suffix}`;
const blockedByPureCorp = `${REMOTE_ORIGIN}${BASE}/nothing-same-origin-corp.txt?${suffix}`;
const blockedDueToCoep = `${REMOTE_ORIGIN}/common/text-plain.txt?abc&${suffix}`;
const dest = `${REMOTE_ORIGIN}/common/text-plain.txt?xyz&${suffix}`;
const redirect = `/common/redirect.py?location=${encodeURIComponent(dest)}&${suffix}`;
fetchInIframe(sameOriginUrl);
fetchInIframe(blockedByPureCorp);
fetchInIframe(blockedDueToCoep);
fetchInIframe(redirect);
// Wait 3 seconds for reports to settle.
await wait(3000);
const contextUrl = SCRIPT;
checkReportNonExistence(reports, sameOriginUrl, contextUrl);
checkReportNonExistence(reports, blockedByPureCorp, contextUrl);
checkCorpReportExistence(reports, blockedDueToCoep, contextUrl);
checkCorpReportExistence(reports, redirect, contextUrl);
checkReportNonExistence(reports, dest, contextUrl);
t.done();
} catch (e) {
t.step(() => { throw e });
}
}, 'CORP for subresource requests initiated from a service worker');
async_test(async (t) => {
try {
const iframe = document.createElement('iframe');
......
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