CSPEvalChecker.cpp (5376B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/CSPEvalChecker.h" 8 9 #include "mozilla/ErrorResult.h" 10 #include "mozilla/dom/Document.h" 11 #include "mozilla/dom/PolicyContainer.h" 12 #include "mozilla/dom/WorkerPrivate.h" 13 #include "mozilla/dom/WorkerRunnable.h" 14 #include "nsCOMPtr.h" 15 #include "nsContentSecurityUtils.h" 16 #include "nsContentUtils.h" 17 #include "nsGlobalWindowInner.h" 18 19 using namespace mozilla; 20 using namespace mozilla::dom; 21 22 namespace { 23 24 // We use the subjectPrincipal to assert that eval() is never 25 // executed in system privileged context. 26 nsresult CheckInternal(nsIContentSecurityPolicy* aCSP, 27 nsICSPEventListener* aCSPEventListener, 28 nsIPrincipal* aSubjectPrincipal, 29 const nsAString& aExpression, 30 const JSCallingLocation& aCaller, bool* aAllowed) { 31 MOZ_ASSERT(NS_IsMainThread()); 32 MOZ_ASSERT(aAllowed); 33 34 // The value is set at any "return", but better to have a default value here. 35 *aAllowed = false; 36 37 // This is the non-CSP check for gating eval() use in the SystemPrincipal 38 JSContext* cx = nsContentUtils::GetCurrentJSContext(); 39 if (!nsContentSecurityUtils::IsEvalAllowed( 40 cx, aSubjectPrincipal->IsSystemPrincipal(), aExpression)) { 41 *aAllowed = false; 42 return NS_OK; 43 } 44 45 if (!aCSP) { 46 *aAllowed = true; 47 return NS_OK; 48 } 49 50 bool reportViolation = false; 51 nsresult rv = aCSP->GetAllowsEval(&reportViolation, aAllowed); 52 if (NS_WARN_IF(NS_FAILED(rv))) { 53 *aAllowed = false; 54 return rv; 55 } 56 57 if (reportViolation) { 58 aCSP->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, 59 nullptr, // triggering element 60 aCSPEventListener, aCaller.FileName(), 61 aExpression, aCaller.mLine, aCaller.mColumn, 62 u""_ns, u""_ns); 63 } 64 65 return NS_OK; 66 } 67 68 class WorkerCSPCheckRunnable final : public WorkerMainThreadRunnable { 69 public: 70 WorkerCSPCheckRunnable(WorkerPrivate* aWorkerPrivate, 71 const nsAString& aExpression, 72 JSCallingLocation&& aCaller) 73 : WorkerMainThreadRunnable(aWorkerPrivate, "CSP Eval Check"_ns), 74 mExpression(aExpression), 75 mCaller(std::move(aCaller)), 76 mEvalAllowed(false) {} 77 78 bool MainThreadRun() override { 79 MOZ_ASSERT(mWorkerRef); 80 WorkerPrivate* workerPrivate = mWorkerRef->Private(); 81 mResult = CheckInternal(workerPrivate->GetCsp(), 82 workerPrivate->CSPEventListener(), 83 workerPrivate->GetLoadingPrincipal(), mExpression, 84 mCaller, &mEvalAllowed); 85 return true; 86 } 87 88 nsresult GetResult(bool* aAllowed) { 89 MOZ_ASSERT(aAllowed); 90 *aAllowed = mEvalAllowed; 91 return mResult; 92 } 93 94 private: 95 const nsString mExpression; 96 const JSCallingLocation mCaller; 97 bool mEvalAllowed; 98 nsresult mResult; 99 }; 100 101 } // namespace 102 103 /* static */ 104 nsresult CSPEvalChecker::CheckForWindow(JSContext* aCx, 105 nsGlobalWindowInner* aWindow, 106 const nsAString& aExpression, 107 bool* aAllowEval) { 108 MOZ_ASSERT(NS_IsMainThread()); 109 MOZ_ASSERT(aWindow); 110 MOZ_ASSERT(aAllowEval); 111 112 // The value is set at any "return", but better to have a default value here. 113 *aAllowEval = false; 114 115 // if CSP is enabled, and setTimeout/setInterval was called with a string, 116 // disable the registration and log an error 117 nsCOMPtr<Document> doc = aWindow->GetExtantDoc(); 118 if (!doc) { 119 // if there's no document, we don't have to do anything. 120 *aAllowEval = true; 121 return NS_OK; 122 } 123 124 nsresult rv = NS_OK; 125 126 auto location = JSCallingLocation::Get(aCx); 127 nsCOMPtr<nsIContentSecurityPolicy> csp = 128 PolicyContainer::GetCSP(doc->GetPolicyContainer()); 129 rv = CheckInternal(csp, nullptr /* no CSPEventListener for window */, 130 doc->NodePrincipal(), aExpression, location, aAllowEval); 131 if (NS_WARN_IF(NS_FAILED(rv))) { 132 *aAllowEval = false; 133 return rv; 134 } 135 136 return NS_OK; 137 } 138 139 /* static */ 140 nsresult CSPEvalChecker::CheckForWorker(JSContext* aCx, 141 WorkerPrivate* aWorkerPrivate, 142 const nsAString& aExpression, 143 bool* aAllowEval) { 144 MOZ_ASSERT(aWorkerPrivate); 145 aWorkerPrivate->AssertIsOnWorkerThread(); 146 MOZ_ASSERT(aAllowEval); 147 148 // The value is set at any "return", but better to have a default value here. 149 *aAllowEval = false; 150 151 RefPtr<WorkerCSPCheckRunnable> r = new WorkerCSPCheckRunnable( 152 aWorkerPrivate, aExpression, JSCallingLocation::Get(aCx)); 153 ErrorResult error; 154 r->Dispatch(aWorkerPrivate, Canceling, error); 155 if (NS_WARN_IF(error.Failed())) { 156 *aAllowEval = false; 157 return error.StealNSResult(); 158 } 159 160 nsresult rv = r->GetResult(aAllowEval); 161 if (NS_WARN_IF(NS_FAILED(rv))) { 162 *aAllowEval = false; 163 return rv; 164 } 165 166 return NS_OK; 167 }