Backed out changeset 0c01a856e4c3 (bug 1870427) as requested by Emilio CLOSED TREE
[gecko.git] / dom / security / CSPEvalChecker.cpp
blob1536b59fca87c85ec44b3161dcdb1889c93647a4
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/. */
7 #include "mozilla/dom/CSPEvalChecker.h"
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/dom/WorkerPrivate.h"
10 #include "mozilla/dom/WorkerRunnable.h"
11 #include "mozilla/ErrorResult.h"
12 #include "nsGlobalWindowInner.h"
13 #include "nsContentSecurityUtils.h"
14 #include "nsContentUtils.h"
15 #include "nsCOMPtr.h"
17 using namespace mozilla;
18 using namespace mozilla::dom;
20 namespace {
22 // We use the subjectPrincipal to assert that eval() is never
23 // executed in system privileged context.
24 nsresult CheckInternal(nsIContentSecurityPolicy* aCSP,
25 nsICSPEventListener* aCSPEventListener,
26 nsIPrincipal* aSubjectPrincipal,
27 const nsAString& aExpression,
28 const JSCallingLocation& aCaller, bool* aAllowed) {
29 MOZ_ASSERT(NS_IsMainThread());
30 MOZ_ASSERT(aAllowed);
32 // The value is set at any "return", but better to have a default value here.
33 *aAllowed = false;
35 // This is the non-CSP check for gating eval() use in the SystemPrincipal
36 #if !defined(ANDROID)
37 JSContext* cx = nsContentUtils::GetCurrentJSContext();
38 if (!nsContentSecurityUtils::IsEvalAllowed(
39 cx, aSubjectPrincipal->IsSystemPrincipal(), aExpression)) {
40 *aAllowed = false;
41 return NS_OK;
43 #endif
45 if (!aCSP) {
46 *aAllowed = true;
47 return NS_OK;
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;
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);
65 return NS_OK;
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) {}
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;
88 nsresult GetResult(bool* aAllowed) {
89 MOZ_ASSERT(aAllowed);
90 *aAllowed = mEvalAllowed;
91 return mResult;
94 private:
95 const nsString mExpression;
96 const JSCallingLocation mCaller;
97 bool mEvalAllowed;
98 nsresult mResult;
101 } // namespace
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);
112 // The value is set at any "return", but better to have a default value here.
113 *aAllowEval = false;
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;
124 nsresult rv = NS_OK;
126 auto location = JSCallingLocation::Get(aCx);
127 nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
128 rv = CheckInternal(csp, nullptr /* no CSPEventListener for window */,
129 doc->NodePrincipal(), aExpression, location, aAllowEval);
130 if (NS_WARN_IF(NS_FAILED(rv))) {
131 *aAllowEval = false;
132 return rv;
135 return NS_OK;
138 /* static */
139 nsresult CSPEvalChecker::CheckForWorker(JSContext* aCx,
140 WorkerPrivate* aWorkerPrivate,
141 const nsAString& aExpression,
142 bool* aAllowEval) {
143 MOZ_ASSERT(aWorkerPrivate);
144 aWorkerPrivate->AssertIsOnWorkerThread();
145 MOZ_ASSERT(aAllowEval);
147 // The value is set at any "return", but better to have a default value here.
148 *aAllowEval = false;
150 RefPtr<WorkerCSPCheckRunnable> r = new WorkerCSPCheckRunnable(
151 aWorkerPrivate, aExpression, JSCallingLocation::Get(aCx));
152 ErrorResult error;
153 r->Dispatch(aWorkerPrivate, Canceling, error);
154 if (NS_WARN_IF(error.Failed())) {
155 *aAllowEval = false;
156 return error.StealNSResult();
159 nsresult rv = r->GetResult(aAllowEval);
160 if (NS_WARN_IF(NS_FAILED(rv))) {
161 *aAllowEval = false;
162 return rv;
165 return NS_OK;