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/BasePrincipal.h"
12 #include "mozilla/ErrorResult.h"
13 #include "nsIParentChannel.h"
14 #include "nsGlobalWindowInner.h"
15 #include "nsContentSecurityUtils.h"
16 #include "nsContentUtils.h"
18 #include "nsJSUtils.h"
20 using namespace mozilla
;
21 using namespace mozilla::dom
;
25 // We use the subjectPrincipal to assert that eval() is never
26 // executed in system privileged context.
27 nsresult
CheckInternal(nsIContentSecurityPolicy
* aCSP
,
28 nsICSPEventListener
* aCSPEventListener
,
29 nsIPrincipal
* aSubjectPrincipal
,
30 const nsAString
& aExpression
,
31 const nsAString
& aFileNameString
, uint32_t aLineNum
,
32 uint32_t aColumnNum
, bool* aAllowed
) {
33 MOZ_ASSERT(NS_IsMainThread());
36 // The value is set at any "return", but better to have a default value here.
39 // This is the non-CSP check for gating eval() use in the SystemPrincipal
41 JSContext
* cx
= nsContentUtils::GetCurrentJSContext();
42 if (!nsContentSecurityUtils::IsEvalAllowed(
43 cx
, aSubjectPrincipal
->IsSystemPrincipal(), aExpression
)) {
54 bool reportViolation
= false;
55 nsresult rv
= aCSP
->GetAllowsEval(&reportViolation
, aAllowed
);
56 if (NS_WARN_IF(NS_FAILED(rv
))) {
61 if (reportViolation
) {
62 aCSP
->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL
,
63 nullptr, // triggering element
64 aCSPEventListener
, aFileNameString
, aExpression
,
65 aLineNum
, aColumnNum
, u
""_ns
, u
""_ns
);
71 class WorkerCSPCheckRunnable final
: public WorkerMainThreadRunnable
{
73 WorkerCSPCheckRunnable(WorkerPrivate
* aWorkerPrivate
,
74 const nsAString
& aExpression
,
75 const nsAString
& aFileNameString
, uint32_t aLineNum
,
77 : WorkerMainThreadRunnable(aWorkerPrivate
, "CSP Eval Check"_ns
),
78 mExpression(aExpression
),
79 mFileNameString(aFileNameString
),
81 mColumnNum(aColumnNum
),
82 mEvalAllowed(false) {}
84 bool MainThreadRun() override
{
85 mResult
= CheckInternal(
86 mWorkerPrivate
->GetCsp(), mWorkerPrivate
->CSPEventListener(),
87 mWorkerPrivate
->GetLoadingPrincipal(), mExpression
, mFileNameString
,
88 mLineNum
, mColumnNum
, &mEvalAllowed
);
92 nsresult
GetResult(bool* aAllowed
) {
94 *aAllowed
= mEvalAllowed
;
99 const nsString mExpression
;
100 const nsString mFileNameString
;
101 const uint32_t mLineNum
;
102 const uint32_t mColumnNum
;
110 nsresult
CSPEvalChecker::CheckForWindow(JSContext
* aCx
,
111 nsGlobalWindowInner
* aWindow
,
112 const nsAString
& aExpression
,
114 MOZ_ASSERT(NS_IsMainThread());
116 MOZ_ASSERT(aAllowEval
);
118 // The value is set at any "return", but better to have a default value here.
121 // if CSP is enabled, and setTimeout/setInterval was called with a string,
122 // disable the registration and log an error
123 nsCOMPtr
<Document
> doc
= aWindow
->GetExtantDoc();
125 // if there's no document, we don't have to do anything.
132 // Get the calling location.
133 uint32_t lineNum
= 0;
134 uint32_t columnNum
= 1;
135 nsAutoString fileNameString
;
136 if (!nsJSUtils::GetCallingLocation(aCx
, fileNameString
, &lineNum
,
138 fileNameString
.AssignLiteral("unknown");
141 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= doc
->GetCsp();
142 rv
= CheckInternal(csp
, nullptr /* no CSPEventListener for window */,
143 doc
->NodePrincipal(), aExpression
, fileNameString
, lineNum
,
144 columnNum
, aAllowEval
);
145 if (NS_WARN_IF(NS_FAILED(rv
))) {
154 nsresult
CSPEvalChecker::CheckForWorker(JSContext
* aCx
,
155 WorkerPrivate
* aWorkerPrivate
,
156 const nsAString
& aExpression
,
158 MOZ_ASSERT(aWorkerPrivate
);
159 aWorkerPrivate
->AssertIsOnWorkerThread();
160 MOZ_ASSERT(aAllowEval
);
162 // The value is set at any "return", but better to have a default value here.
165 // Get the calling location.
166 uint32_t lineNum
= 0;
167 uint32_t columnNum
= 1;
168 nsAutoString fileNameString
;
169 if (!nsJSUtils::GetCallingLocation(aCx
, fileNameString
, &lineNum
,
171 fileNameString
.AssignLiteral("unknown");
174 RefPtr
<WorkerCSPCheckRunnable
> r
= new WorkerCSPCheckRunnable(
175 aWorkerPrivate
, aExpression
, fileNameString
, lineNum
, columnNum
);
177 r
->Dispatch(Canceling
, error
);
178 if (NS_WARN_IF(error
.Failed())) {
180 return error
.StealNSResult();
183 nsresult rv
= r
->GetResult(aAllowEval
);
184 if (NS_WARN_IF(NS_FAILED(rv
))) {