author | Andrea Marchesini <amarchesini@mozilla.com> |
Mon, 17 Sep 2018 17:53:10 +0200 | |
changeset 436808 | ebcf49549e65feea89be2cc429b7ff8a4e59e509 |
parent 436807 | 3b43ef0d83a0c18c0f46d822d8a2bfc28ad5c0ca |
child 436809 | 28025c893d7853142edd91840ea801b6f599b8be |
push id | 34660 |
push user | btara@mozilla.com |
push date | Mon, 17 Sep 2018 21:58:52 +0000 |
treeherder | mozilla-central@87a95e1b7ec6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ckerschb |
bugs | 1490165 |
milestone | 64.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -4,16 +4,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <algorithm> #include "mozilla/Attributes.h" #include "mozilla/Likely.h" #include "mozilla/Maybe.h" +#include "mozilla/dom/CSPEvalChecker.h" #include "mozilla/dom/FunctionBinding.h" #include "mozilla/dom/WorkerPrivate.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsError.h" #include "nsGlobalWindow.h" #include "nsIContentSecurityPolicy.h" #include "nsIDocument.h" @@ -40,17 +41,18 @@ public: ErrorResult& aError); nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow, const nsAString& aExpression, bool* aAllowEval, ErrorResult& aError); nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate, Function& aFunction, nsTArray<JS::Heap<JS::Value>>&& aArguments); nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate, - const nsAString& aExpression); + const nsAString& aExpression, bool* aAllowEval, + ErrorResult& aRv); virtual const nsAString& GetHandlerText() override; virtual Function* GetCallback() override { return mFunction; } @@ -158,64 +160,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler) NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler) -static bool -CheckCSPForEval(JSContext* aCx, nsGlobalWindowInner* aWindow, - const nsAString& aExpression, ErrorResult& aError) -{ - // if CSP is enabled, and setTimeout/setInterval was called with a string, - // disable the registration and log an error - nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc(); - if (!doc) { - // if there's no document, we don't have to do anything. - return true; - } - - nsCOMPtr<nsIContentSecurityPolicy> csp; - aError = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); - if (aError.Failed()) { - return false; - } - - if (!csp) { - return true; - } - - bool allowsEval = true; - bool reportViolation = false; - aError = csp->GetAllowsEval(&reportViolation, &allowsEval); - if (aError.Failed()) { - return false; - } - - if (reportViolation) { - // Get the calling location. - uint32_t lineNum = 0; - uint32_t columnNum = 0; - nsAutoString fileNameString; - if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum, - &columnNum)) { - fileNameString.AssignLiteral("unknown"); - } - - csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, - nullptr, // triggering element - fileNameString, aExpression, lineNum, columnNum, - EmptyString(), EmptyString()); - } - - return allowsEval; -} - nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() : mLineNo(0) , mColumn(0) { } nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner *aWindow, @@ -247,18 +201,19 @@ nsJSScriptTimeoutHandler::nsJSScriptTime { if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) { // This window was already closed, or never properly initialized, // don't let a timer be scheduled on such a window. aError.Throw(NS_ERROR_NOT_INITIALIZED); return; } - *aAllowEval = CheckCSPForEval(aCx, aWindow, aExpression, aError); - if (aError.Failed() || !*aAllowEval) { + aError = CSPEvalChecker::CheckForWindow(aCx, aWindow, aExpression, + aAllowEval); + if (NS_WARN_IF(aError.Failed()) || !*aAllowEval) { return; } Init(aCx); } nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate, @@ -271,24 +226,32 @@ nsJSScriptTimeoutHandler::nsJSScriptTime MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); Init(aCx, std::move(aArguments)); } nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate, - const nsAString& aExpression) + const nsAString& aExpression, + bool* aAllowEval, + ErrorResult& aError) : mLineNo(0) , mColumn(0) , mExpr(aExpression) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); + aError = CSPEvalChecker::CheckForWorker(aCx, aWorkerPrivate, aExpression, + aAllowEval); + if (NS_WARN_IF(aError.Failed()) || !*aAllowEval) { + return; + } + Init(aCx); } nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler() { ReleaseJSObjects(); } @@ -371,14 +334,20 @@ NS_CreateJSTimeoutHandler(JSContext *aCx RefPtr<nsJSScriptTimeoutHandler> handler = new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aFunction, std::move(args)); return handler.forget(); } already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate, - const nsAString& aExpression) + const nsAString& aExpression, ErrorResult& aRv) { + bool allowEval = false; RefPtr<nsJSScriptTimeoutHandler> handler = - new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aExpression); + new nsJSScriptTimeoutHandler(aCx, aWorkerPrivate, aExpression, &allowEval, + aRv); + if (aRv.Failed() || !allowEval) { + return nullptr; + } + return handler.forget(); }
new file mode 100644 --- /dev/null +++ b/dom/security/CSPEvalChecker.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/CSPEvalChecker.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRunnable.h" +#include "mozilla/ErrorResult.h" +#include "nsGlobalWindowInner.h" +#include "nsIDocument.h" +#include "nsCOMPtr.h" +#include "nsJSUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +namespace { + +nsresult +CheckInternal(nsIContentSecurityPolicy* aCSP, + const nsAString& aExpression, + const nsAString& aFileNameString, + uint32_t aLineNum, + uint32_t aColumnNum, + bool* aAllowed) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aAllowed); + + // The value is set at any "return", but better to have a default value here. + *aAllowed = false; + + if (!aCSP) { + *aAllowed = true; + return NS_OK; + } + + bool reportViolation = false; + nsresult rv = aCSP->GetAllowsEval(&reportViolation, aAllowed); + if (NS_WARN_IF(NS_FAILED(rv))) { + *aAllowed = false; + return rv; + } + + if (reportViolation) { + aCSP->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, + nullptr, // triggering element + aFileNameString, aExpression, aLineNum, + aColumnNum, EmptyString(), EmptyString()); + } + + return NS_OK; +} + +class WorkerCSPCheckRunnable final : public WorkerMainThreadRunnable +{ +public: + WorkerCSPCheckRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aExpression, + const nsAString& aFileNameString, + uint32_t aLineNum, + uint32_t aColumnNum) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("CSP Eval Check")) + , mExpression(aExpression) + , mFileNameString(aFileNameString) + , mLineNum(aLineNum) + , mColumnNum(aColumnNum) + , mEvalAllowed(false) + {} + + bool + MainThreadRun() override + { + mResult = CheckInternal(mWorkerPrivate->GetCSP(), mExpression, + mFileNameString, mLineNum, mColumnNum, + &mEvalAllowed); + return true; + } + + nsresult + GetResult(bool* aAllowed) + { + MOZ_ASSERT(aAllowed); + *aAllowed = mEvalAllowed; + return mResult; + } + +private: + const nsString mExpression; + const nsString mFileNameString; + const uint32_t mLineNum; + const uint32_t mColumnNum; + bool mEvalAllowed; + nsresult mResult; +}; + +} // anonymous + +/* static */ nsresult +CSPEvalChecker::CheckForWindow(JSContext* aCx, nsGlobalWindowInner* aWindow, + const nsAString& aExpression, bool* aAllowEval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aAllowEval); + + // The value is set at any "return", but better to have a default value here. + *aAllowEval = false; + + // if CSP is enabled, and setTimeout/setInterval was called with a string, + // disable the registration and log an error + nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc(); + if (!doc) { + // if there's no document, we don't have to do anything. + *aAllowEval = true; + return NS_OK; + } + + nsCOMPtr<nsIContentSecurityPolicy> csp; + nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); + if (NS_WARN_IF(NS_FAILED(rv))) { + *aAllowEval = false; + return rv; + } + + // Get the calling location. + uint32_t lineNum = 0; + uint32_t columnNum = 0; + nsAutoString fileNameString; + if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum, + &columnNum)) { + fileNameString.AssignLiteral("unknown"); + } + + rv = CheckInternal(csp, aExpression, fileNameString, lineNum, columnNum, + aAllowEval); + if (NS_WARN_IF(NS_FAILED(rv))) { + *aAllowEval = false; + return rv; + } + + return NS_OK; +} + +/* static */ nsresult +CSPEvalChecker::CheckForWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + const nsAString& aExpression, bool* aAllowEval) +{ + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(aAllowEval); + + // The value is set at any "return", but better to have a default value here. + *aAllowEval = false; + + // Get the calling location. + uint32_t lineNum = 0; + uint32_t columnNum = 0; + nsAutoString fileNameString; + if (!nsJSUtils::GetCallingLocation(aCx, fileNameString, &lineNum, + &columnNum)) { + fileNameString.AssignLiteral("unknown"); + } + + RefPtr<WorkerCSPCheckRunnable> r = + new WorkerCSPCheckRunnable(aWorkerPrivate, aExpression, fileNameString, + lineNum, columnNum); + ErrorResult error; + r->Dispatch(Canceling, error); + if (NS_WARN_IF(error.Failed())) { + *aAllowEval = false; + return error.StealNSResult(); + } + + nsresult rv = r->GetResult(aAllowEval); + if (NS_WARN_IF(NS_FAILED(rv))) { + *aAllowEval = false; + return rv; + } + + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/dom/security/CSPEvalChecker.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_CSPEvalChecker_h +#define mozilla_dom_CSPEvalChecker_h + +#include "nsString.h" + +struct JSContext; +class nsGlobalWindowInner; + +namespace mozilla { +namespace dom { + +class WorkerPrivate; + +class CSPEvalChecker final +{ +public: + static nsresult + CheckForWindow(JSContext* aCx, nsGlobalWindowInner* aWindow, + const nsAString& aExpression, bool* aAllowEval); + + static nsresult + CheckForWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + const nsAString& aExpression, bool* aAllowEval); +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_CSPEvalChecker_h
--- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -6,16 +6,17 @@ with Files('*'): BUG_COMPONENT = ('Core', 'DOM: Security') TEST_DIRS += ['test'] EXPORTS.mozilla.dom += [ 'ContentVerifier.h', + 'CSPEvalChecker.h', 'FramingChecker.h', 'nsContentSecurityManager.h', 'nsCSPContext.h', 'nsCSPService.h', 'nsCSPUtils.h', 'nsMixedContentBlocker.h', 'PolicyTokenizer.h', 'SRICheck.h', @@ -25,16 +26,17 @@ EXPORTS.mozilla.dom += [ EXPORTS += [ 'nsContentSecurityManager.h', 'nsMixedContentBlocker.h', ] UNIFIED_SOURCES += [ 'ContentVerifier.cpp', + 'CSPEvalChecker.cpp', 'FramingChecker.cpp', 'nsContentSecurityManager.cpp', 'nsCSPContext.cpp', 'nsCSPParser.cpp', 'nsCSPService.cpp', 'nsCSPUtils.cpp', 'nsMixedContentBlocker.cpp', 'PolicyTokenizer.cpp',
--- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1219,17 +1219,17 @@ private: // importScripts() and the Worker constructor do not support integrity metadata // (or any fetch options). Until then, we can just block. // If we ever have those data in the future, we'll have to the check to // by using the SRICheck module MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, ("Scriptloader::Load, SRI required but not supported in workers")); nsCOMPtr<nsIContentSecurityPolicy> wcsp; chanLoadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(wcsp)); - MOZ_ASSERT(wcsp, "We sould have a CSP for the worker here"); + MOZ_ASSERT(wcsp, "We should have a CSP for the worker here"); if (wcsp) { wcsp->LogViolationDetails( nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT, nullptr, // triggering element aLoadInfo.mURL, EmptyString(), 0, 0, EmptyString(), EmptyString()); } return NS_ERROR_SRI_CORRUPT; }
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -62,17 +62,18 @@ NS_CreateJSTimeoutHandler(JSContext* aCx mozilla::dom::WorkerPrivate* aWorkerPrivate, mozilla::dom::Function& aFunction, const mozilla::dom::Sequence<JS::Value>& aArguments, mozilla::ErrorResult& aError); extern already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(JSContext* aCx, mozilla::dom::WorkerPrivate* aWorkerPrivate, - const nsAString& aExpression); + const nsAString& aExpression, + mozilla::ErrorResult& aRv); namespace mozilla { namespace dom { using mozilla::dom::cache::CacheStorage; using mozilla::ipc::PrincipalInfo; WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate) @@ -271,34 +272,38 @@ WorkerGlobalScope::SetTimeout(JSContext* const int32_t aTimeout, const Sequence<JS::Value>& aArguments, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); nsCOMPtr<nsIScriptTimeoutHandler> handler = NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv); - if (NS_WARN_IF(aRv.Failed())) { + if (!handler) { return 0; } return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv); } int32_t WorkerGlobalScope::SetTimeout(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout, const Sequence<JS::Value>& /* unused */, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); nsCOMPtr<nsIScriptTimeoutHandler> handler = - NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler); + NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aRv); + if (!handler) { + return 0; + } + return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv); } void WorkerGlobalScope::ClearTimeout(int32_t aHandle) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle); @@ -329,17 +334,21 @@ WorkerGlobalScope::SetInterval(JSContext const Sequence<JS::Value>& /* unused */, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); Sequence<JS::Value> dummy; nsCOMPtr<nsIScriptTimeoutHandler> handler = - NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler); + NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return 0; + } + return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, true, aRv); } void WorkerGlobalScope::ClearInterval(int32_t aHandle) { mWorkerPrivate->AssertIsOnWorkerThread(); mWorkerPrivate->ClearTimeout(aHandle);
--- a/testing/web-platform/meta/content-security-policy/inside-worker/dedicated-script.html.ini +++ b/testing/web-platform/meta/content-security-policy/inside-worker/dedicated-script.html.ini @@ -22,14 +22,8 @@ expected: FAIL [`eval()` blocked in http:?pipe=sub|header(Content-Security-Policy,default-src%20*)] expected: FAIL [`eval()` blocked in http:?pipe=sub|header(Content-Security-Policy,script-src%20*)] expected: FAIL - [`setTimeout([string\])` blocked in http:?pipe=sub|header(Content-Security-Policy,default-src%20*)] - expected: FAIL - - [`setTimeout([string\])` blocked in http:?pipe=sub|header(Content-Security-Policy,script-src%20*)] - expected: FAIL -
--- a/testing/web-platform/meta/content-security-policy/inside-worker/shared-script.html.ini +++ b/testing/web-platform/meta/content-security-policy/inside-worker/shared-script.html.ini @@ -1,13 +1,7 @@ [shared-script.html] [`eval()` blocked in http:?pipe=sub|header(Content-Security-Policy,script-src%20%27self%27] expected: FAIL - [`setTimeout([string\])` blocked in http:?pipe=sub|header(Content-Security-Policy,script-src%20%27self%27] - expected: FAIL - [`eval()` blocked in http:?pipe=sub|header(Content-Security-Policy,default-src%20%27self%27] expected: FAIL - [`setTimeout([string\])` blocked in http:?pipe=sub|header(Content-Security-Policy,default-src%20%27self%27] - expected: FAIL -