зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1575090 - set COEP for all workers and enforce it when loading Dedicated Workers r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D46177 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
eb5d4df193
Коммит
8140fd1dc1
|
@ -71,6 +71,8 @@
|
|||
#include "mozilla/dom/SRILogHelper.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
#include "mozilla/dom/ServiceWorkerManager.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_security.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
@ -491,8 +493,8 @@ class CacheScriptLoader final : public PromiseNativeHandler,
|
|||
|
||||
ScriptLoadInfo& mLoadInfo;
|
||||
uint32_t mIndex;
|
||||
RefPtr<ScriptLoaderRunnable> mRunnable;
|
||||
bool mIsWorkerScript;
|
||||
const RefPtr<ScriptLoaderRunnable> mRunnable;
|
||||
const bool mIsWorkerScript;
|
||||
bool mFailed;
|
||||
const ServiceWorkerState mState;
|
||||
nsCOMPtr<nsIInputStreamPump> mPump;
|
||||
|
@ -567,13 +569,86 @@ class LoaderListener final : public nsIStreamLoaderObserver,
|
|||
|
||||
NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
|
||||
|
||||
class ScriptResponseHeaderProcessor final : public nsIRequestObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
ScriptResponseHeaderProcessor(WorkerPrivate* aWorkerPrivate,
|
||||
bool aIsMainScript)
|
||||
: mWorkerPrivate(aWorkerPrivate), mIsMainScript(aIsMainScript) {
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override {
|
||||
nsresult rv = ProcessCrossOriginEmbedderPolicyHeader(aRequest);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRequest->Cancel(rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnStopRequest(nsIRequest* aRequest,
|
||||
nsresult aStatusCode) override {
|
||||
MOZ_DIAGNOSTIC_ASSERT_IF(NS_SUCCEEDED(aStatusCode),
|
||||
mWorkerPrivate->GetEmbedderPolicy().isSome());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult ProcessCrossOriginEmbedderPolicyHeader(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy aPolicy, bool aIsMainScript) {
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
if (aIsMainScript) {
|
||||
MOZ_TRY(aWorkerPrivate->SetEmbedderPolicy(aPolicy));
|
||||
} else if (!aWorkerPrivate->MatchEmbedderPolicy(aPolicy)) {
|
||||
return NS_ERROR_BLOCKED_BY_POLICY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~ScriptResponseHeaderProcessor() = default;
|
||||
|
||||
nsresult ProcessCrossOriginEmbedderPolicyHeader(nsIRequest* aRequest) {
|
||||
MOZ_ASSERT_IF(!mIsMainScript, mWorkerPrivate->GetEmbedderPolicy().isSome());
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aRequest);
|
||||
|
||||
// NOTE: the spec doesn't say what to do with non-HTTP workers.
|
||||
// See: https://github.com/whatwg/html/issues/4916
|
||||
if (!httpChannel) {
|
||||
if (mIsMainScript) {
|
||||
mWorkerPrivate->InheritOwnerEmbedderPolicyOrNull(aRequest);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy coep;
|
||||
MOZ_TRY(httpChannel->GetResponseEmbedderPolicy(&coep));
|
||||
|
||||
return ProcessCrossOriginEmbedderPolicyHeader(mWorkerPrivate, coep,
|
||||
mIsMainScript);
|
||||
}
|
||||
|
||||
WorkerPrivate* const mWorkerPrivate;
|
||||
const bool mIsMainScript;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ScriptResponseHeaderProcessor, nsIRequestObserver);
|
||||
|
||||
class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
||||
friend class ScriptExecutorRunnable;
|
||||
friend class CachePromiseHandler;
|
||||
friend class CacheScriptLoader;
|
||||
friend class LoaderListener;
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
WorkerPrivate* const mWorkerPrivate;
|
||||
UniquePtr<SerializedStackHolder> mOriginStack;
|
||||
nsString mOriginStackJSON;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
@ -581,7 +656,7 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
RefPtr<CacheCreator> mCacheCreator;
|
||||
Maybe<ClientInfo> mClientInfo;
|
||||
Maybe<ServiceWorkerDescriptor> mController;
|
||||
bool mIsMainScript;
|
||||
const bool mIsMainScript;
|
||||
WorkerScriptType mWorkerScriptType;
|
||||
bool mCanceledMainThread;
|
||||
ErrorResult& mRv;
|
||||
|
@ -680,13 +755,22 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
}
|
||||
|
||||
nsresult OnStartRequest(nsIRequest* aRequest, uint32_t aIndex) {
|
||||
nsresult rv = OnStartRequestInternal(aRequest, aIndex);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRequest->Cancel(rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult OnStartRequestInternal(nsIRequest* aRequest, uint32_t aIndex) {
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aIndex < mLoadInfos.Length());
|
||||
|
||||
// If one load info cancels or hits an error, it can race with the start
|
||||
// callback coming from another load info.
|
||||
if (mCanceledMainThread || !mCacheCreator) {
|
||||
aRequest->Cancel(NS_ERROR_FAILURE);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -715,7 +799,6 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
nsTArray<nsString>{NS_ConvertUTF8toUTF16(scope),
|
||||
NS_ConvertUTF8toUTF16(mimeType), loadInfo.mURL});
|
||||
|
||||
channel->Cancel(NS_ERROR_DOM_NETWORK_ERR);
|
||||
return NS_ERROR_DOM_NETWORK_ERR;
|
||||
}
|
||||
}
|
||||
|
@ -745,19 +828,11 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
NS_ASSERTION(ssm, "Should never be null!");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> channelPrincipal;
|
||||
nsresult rv = ssm->GetChannelResultPrincipal(
|
||||
channel, getter_AddRefs(channelPrincipal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
channel->Cancel(rv);
|
||||
return rv;
|
||||
}
|
||||
MOZ_TRY(ssm->GetChannelResultPrincipal(channel,
|
||||
getter_AddRefs(channelPrincipal)));
|
||||
|
||||
UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
|
||||
rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
channel->Cancel(rv);
|
||||
return rv;
|
||||
}
|
||||
MOZ_TRY(PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get()));
|
||||
|
||||
ir->SetPrincipalInfo(std::move(principalInfo));
|
||||
ir->Headers()->FillResponseHeaders(loadInfo.mChannel);
|
||||
|
@ -781,9 +856,7 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
|
||||
error.WouldReportJSException();
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
nsresult rv = error.StealNSResult();
|
||||
channel->Cancel(rv);
|
||||
return rv;
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
RefPtr<CachePromiseHandler> promiseHandler =
|
||||
|
@ -1016,10 +1089,17 @@ class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
|
|||
// where to put the result.
|
||||
RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
|
||||
|
||||
// We don't care about progress so just use the simple stream loader for
|
||||
// OnStreamComplete notification only.
|
||||
RefPtr<ScriptResponseHeaderProcessor> headerProcessor = nullptr;
|
||||
|
||||
// For each debugger script, a non-debugger script load of the same script
|
||||
// should have occured prior that processed the headers.
|
||||
if (!IsDebuggerScript()) {
|
||||
headerProcessor = MakeRefPtr<ScriptResponseHeaderProcessor>(
|
||||
mWorkerPrivate, mIsMainScript);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), listener, headerProcessor);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -1749,6 +1829,25 @@ void CacheScriptLoader::ResolvedCallback(JSContext* aCx,
|
|||
headers->Get(NS_LITERAL_CSTRING("referrer-policy"),
|
||||
mReferrerPolicyHeaderValue, IgnoreErrors());
|
||||
|
||||
nsAutoCString coepHeader;
|
||||
headers->Get(NS_LITERAL_CSTRING("cross-origin-embedder-policy"), coepHeader,
|
||||
IgnoreErrors());
|
||||
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy coep =
|
||||
nsILoadInfo::EMBEDDER_POLICY_NULL;
|
||||
|
||||
if (coepHeader.EqualsLiteral("require-corp")) {
|
||||
coep = nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP;
|
||||
}
|
||||
|
||||
rv = ScriptResponseHeaderProcessor::ProcessCrossOriginEmbedderPolicyHeader(
|
||||
mRunnable->mWorkerPrivate, coep, mRunnable->mIsMainScript);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Fail(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
response->GetBody(getter_AddRefs(inputStream));
|
||||
mChannelInfo = response->GetChannelInfo();
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "js/SourceText.h"
|
||||
#include "MessageEventRunnable.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/StaticPrefs_browser.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/dom/CallbackDebuggerNotification.h"
|
||||
|
@ -4992,6 +4993,84 @@ bool WorkerPrivate::CanShareMemory(const nsID& aAgentClusterId) {
|
|||
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
|
||||
}
|
||||
|
||||
Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> WorkerPrivate::GetEmbedderPolicy()
|
||||
const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
|
||||
return Some(nsILoadInfo::EMBEDDER_POLICY_NULL);
|
||||
}
|
||||
|
||||
return mEmbedderPolicy;
|
||||
}
|
||||
|
||||
Result<Ok, nsresult> WorkerPrivate::SetEmbedderPolicy(
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
if (GetOwnerEmbedderPolicy().valueOr(aPolicy) != aPolicy) {
|
||||
return Err(NS_ERROR_BLOCKED_BY_POLICY);
|
||||
}
|
||||
|
||||
mEmbedderPolicy.emplace(aPolicy);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aRequest);
|
||||
|
||||
auto coep = GetOwnerEmbedderPolicy();
|
||||
|
||||
if (coep.isSome()) {
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
MOZ_ASSERT(channel);
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI;
|
||||
MOZ_ALWAYS_SUCCEEDS(channel->GetURI(getter_AddRefs(scriptURI)));
|
||||
|
||||
bool isLocalScriptURI = false;
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_URIChainHasFlags(
|
||||
scriptURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
|
||||
&isLocalScriptURI));
|
||||
|
||||
MOZ_RELEASE_ASSERT(isLocalScriptURI);
|
||||
}
|
||||
|
||||
mEmbedderPolicy.emplace(coep.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL));
|
||||
}
|
||||
|
||||
bool WorkerPrivate::MatchEmbedderPolicy(
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mEmbedderPolicy.value() == aPolicy;
|
||||
}
|
||||
|
||||
Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>
|
||||
WorkerPrivate::GetOwnerEmbedderPolicy() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (GetParent()) {
|
||||
return GetParent()->GetEmbedderPolicy();
|
||||
}
|
||||
|
||||
if (GetWindow() && GetWindow()->GetBrowsingContext()) {
|
||||
return Some(GetWindow()->GetBrowsingContext()->GetEmbedderPolicy());
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
|
||||
NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
|
||||
|
||||
|
|
|
@ -7,18 +7,23 @@
|
|||
#ifndef mozilla_dom_workers_workerprivate_h__
|
||||
#define mozilla_dom_workers_workerprivate_h__
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerStatus.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/RelativeTimeline.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/StorageAccess.h"
|
||||
#include "mozilla/ThreadSafeWeakPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
#include "js/ContextOptions.h"
|
||||
|
@ -712,7 +717,7 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
return mLoadInfo.mChannel.forget();
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* GetWindow() {
|
||||
nsPIDOMWindowInner* GetWindow() const {
|
||||
AssertIsOnMainThread();
|
||||
return mLoadInfo.mWindow;
|
||||
}
|
||||
|
@ -902,6 +907,34 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
return mAgentClusterOpenerPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* COEP Methods
|
||||
*
|
||||
* If browser.tabs.remote.useCrossOriginEmbedderPolicy=false, these methods
|
||||
* will, depending on the return type, return a value that will avoid
|
||||
* assertion failures or a value that won't block loads.
|
||||
*/
|
||||
|
||||
Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> GetEmbedderPolicy() const;
|
||||
|
||||
// Fails if a policy has already been set or if `aPolicy` violates the owner's
|
||||
// policy, if an owner exists.
|
||||
mozilla::Result<Ok, nsresult> SetEmbedderPolicy(
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy aPolicy);
|
||||
|
||||
// `aRequest` is the request loading the worker and must be QI-able to
|
||||
// `nsIChannel*`. It's used to verify that the worker can indeed inherit its
|
||||
// owner's COEP (when an owner exists).
|
||||
//
|
||||
// TODO: remove `aRequest`; currently, it's required because instances may not
|
||||
// always know its final, resolved script URL or have access internally to
|
||||
// `aRequest`.
|
||||
void InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest);
|
||||
|
||||
// Requires a policy to already have been set.
|
||||
bool MatchEmbedderPolicy(
|
||||
nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const;
|
||||
|
||||
private:
|
||||
WorkerPrivate(
|
||||
WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
|
@ -997,6 +1030,8 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
// executed.
|
||||
void DispatchCancelingRunnable();
|
||||
|
||||
Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> GetOwnerEmbedderPolicy() const;
|
||||
|
||||
class EventTarget;
|
||||
friend class EventTarget;
|
||||
friend class AutoSyncLoopHolder;
|
||||
|
@ -1011,9 +1046,9 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
SharedMutex mMutex;
|
||||
mozilla::CondVar mCondVar;
|
||||
|
||||
WorkerPrivate* mParent;
|
||||
WorkerPrivate* const mParent;
|
||||
|
||||
nsString mScriptURL;
|
||||
const nsString mScriptURL;
|
||||
|
||||
// This is the worker name for shared workers and dedicated workers.
|
||||
nsString mWorkerName;
|
||||
|
@ -1220,6 +1255,14 @@ class WorkerPrivate : public RelativeTimeline {
|
|||
// This is used to check if it's allowed to share the memory across the agent
|
||||
// cluster.
|
||||
const nsILoadInfo::CrossOriginOpenerPolicy mAgentClusterOpenerPolicy;
|
||||
|
||||
// Member variable of this class rather than the worker global scope because
|
||||
// it's received on the main thread, but the global scope is thread-bound
|
||||
// to the worker thread, so storing the value in the global scope would
|
||||
// involve sacrificing the thread-bound-ness or using a WorkerRunnable, and
|
||||
// there isn't a strong reason to store it on the global scope other than
|
||||
// better consistency with the COEP spec.
|
||||
Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> mEmbedderPolicy;
|
||||
};
|
||||
|
||||
class AutoSyncLoopHolder {
|
||||
|
|
Загрузка…
Ссылка в новой задаче