1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/frame/csp/execution_context_csp_delegate.h"
6
7 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
8 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
9 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
10 #include "third_party/blink/renderer/core/dom/document.h"
11 #include "third_party/blink/renderer/core/events/security_policy_violation_event.h"
12 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
13 #include "third_party/blink/renderer/core/execution_context/security_context.h"
14 #include "third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h"
15 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
16 #include "third_party/blink/renderer/core/frame/local_frame.h"
17 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
18 #include "third_party/blink/renderer/core/frame/report.h"
19 #include "third_party/blink/renderer/core/frame/reporting_context.h"
20 #include "third_party/blink/renderer/core/loader/document_loader.h"
21 #include "third_party/blink/renderer/core/loader/ping_loader.h"
22 #include "third_party/blink/renderer/core/probe/core_probes.h"
23 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
24 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
25 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
26 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
27 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
28
29 namespace blink {
30
ExecutionContextCSPDelegate(ExecutionContext & execution_context)31 ExecutionContextCSPDelegate::ExecutionContextCSPDelegate(
32 ExecutionContext& execution_context)
33 : execution_context_(&execution_context) {}
34
Trace(Visitor * visitor)35 void ExecutionContextCSPDelegate::Trace(Visitor* visitor) {
36 visitor->Trace(execution_context_);
37 ContentSecurityPolicyDelegate::Trace(visitor);
38 }
39
GetSecurityOrigin()40 const SecurityOrigin* ExecutionContextCSPDelegate::GetSecurityOrigin() {
41 return execution_context_->GetSecurityOrigin();
42 }
43
GetSecureContextMode()44 SecureContextMode ExecutionContextCSPDelegate::GetSecureContextMode() {
45 return GetSecurityContext().GetSecureContextMode();
46 }
47
Url() const48 const KURL& ExecutionContextCSPDelegate::Url() const {
49 return execution_context_->Url();
50 }
51
SetSandboxFlags(SandboxFlags mask)52 void ExecutionContextCSPDelegate::SetSandboxFlags(SandboxFlags mask) {
53 // Ideally sandbox flags are determined at construction time since
54 // sandbox flags influence the security origin and that influences
55 // the Agent that is assigned for the ExecutionContext. Changing
56 // an ExecutionContext's agent in the middle of an object lifecycle
57 // is not permitted.
58
59 // Since Workers and Worklets don't share agents (each one is unique)
60 // we allow them to apply new sandbox flags on top of the current ones.
61 WorkerOrWorkletGlobalScope* worklet_or_worker =
62 DynamicTo<WorkerOrWorkletGlobalScope>(execution_context_.Get());
63 if (worklet_or_worker) {
64 worklet_or_worker->ApplySandboxFlags(mask);
65 }
66 // Just check that all the sandbox flags that are set by CSP have
67 // already been set on the security context. Meta tags can't set them
68 // and we should have already constructed the document with the correct
69 // sandbox flags from CSP already.
70 mojom::blink::WebSandboxFlags flags = GetSecurityContext().GetSandboxFlags();
71 CHECK_EQ(flags | mask, flags);
72 }
73
SetRequireTrustedTypes()74 void ExecutionContextCSPDelegate::SetRequireTrustedTypes() {
75 GetSecurityContext().SetRequireTrustedTypes();
76 }
77
AddInsecureRequestPolicy(mojom::blink::InsecureRequestPolicy policy)78 void ExecutionContextCSPDelegate::AddInsecureRequestPolicy(
79 mojom::blink::InsecureRequestPolicy policy) {
80 SecurityContext& security_context = GetSecurityContext();
81
82 Document* document = GetDocument();
83
84 // Step 2. Set settings’s insecure requests policy to Upgrade. [spec text]
85 // Upgrade Insecure Requests: Update the policy.
86 security_context.SetInsecureRequestPolicy(
87 security_context.GetInsecureRequestPolicy() | policy);
88 if (document)
89 document->DidEnforceInsecureRequestPolicy();
90
91 // Upgrade Insecure Requests: Update the set of insecure URLs to upgrade.
92 if ((policy &
93 mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
94 mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone) {
95 // Spec: Enforcing part of:
96 // https://w3c.github.io/webappsec-upgrade-insecure-requests/#delivery
97 // Step 3. Let tuple be a tuple of the protected resource’s URL's host and
98 // port. [spec text]
99 // Step 4. Insert tuple into settings’s upgrade insecure navigations set.
100 // [spec text]
101 Count(WebFeature::kUpgradeInsecureRequestsEnabled);
102 // We don't add the hash if |document| is null, to prevent
103 // WorkerGlobalScope::Url() before it's ready. https://crbug.com/861564
104 // This should be safe, because the insecure navigations set is not used
105 // in non-Document contexts.
106 if (document && !Url().Host().IsEmpty()) {
107 uint32_t hash = Url().Host().Impl()->GetHash();
108 security_context.AddInsecureNavigationUpgrade(hash);
109 document->DidEnforceInsecureNavigationsSet();
110 }
111 }
112 }
113
114 std::unique_ptr<SourceLocation>
GetSourceLocation()115 ExecutionContextCSPDelegate::GetSourceLocation() {
116 return SourceLocation::Capture(execution_context_);
117 }
118
GetStatusCode()119 base::Optional<uint16_t> ExecutionContextCSPDelegate::GetStatusCode() {
120 base::Optional<uint16_t> status_code;
121
122 // TODO(mkwst): We only have status code information for Documents. It would
123 // be nice to get them for Workers as well.
124 Document* document = GetDocument();
125 if (document && !SecurityOrigin::IsSecure(document->Url()) &&
126 document->Loader()) {
127 status_code = document->Loader()->GetResponse().HttpStatusCode();
128 }
129
130 return status_code;
131 }
132
GetDocumentReferrer()133 String ExecutionContextCSPDelegate::GetDocumentReferrer() {
134 String referrer;
135
136 // TODO(mkwst): We only have referrer information for Documents. It would be
137 // nice to get them for Workers as well.
138 if (Document* document = GetDocument())
139 referrer = document->referrer();
140 return referrer;
141 }
142
DispatchViolationEvent(const SecurityPolicyViolationEventInit & violation_data,Element * element)143 void ExecutionContextCSPDelegate::DispatchViolationEvent(
144 const SecurityPolicyViolationEventInit& violation_data,
145 Element* element) {
146 execution_context_->GetTaskRunner(TaskType::kNetworking)
147 ->PostTask(
148 FROM_HERE,
149 WTF::Bind(
150 &ExecutionContextCSPDelegate::DispatchViolationEventInternal,
151 WrapPersistent(this), WrapPersistent(&violation_data),
152 WrapPersistent(element)));
153 }
154
PostViolationReport(const SecurityPolicyViolationEventInit & violation_data,const String & stringified_report,bool is_frame_ancestors_violation,const Vector<String> & report_endpoints,bool use_reporting_api)155 void ExecutionContextCSPDelegate::PostViolationReport(
156 const SecurityPolicyViolationEventInit& violation_data,
157 const String& stringified_report,
158 bool is_frame_ancestors_violation,
159 const Vector<String>& report_endpoints,
160 bool use_reporting_api) {
161 DCHECK_EQ(is_frame_ancestors_violation,
162 ContentSecurityPolicy::DirectiveType::kFrameAncestors ==
163 ContentSecurityPolicy::GetDirectiveType(
164 violation_data.effectiveDirective()));
165
166 // TODO(crbug/929370): Support POSTing violation reports from a Worker.
167 Document* document = GetDocument();
168 if (!document)
169 return;
170
171 LocalFrame* frame = document->GetFrame();
172 if (!frame)
173 return;
174
175 scoped_refptr<EncodedFormData> report =
176 EncodedFormData::Create(stringified_report.Utf8());
177
178 // Construct and route the report to the ReportingContext, to be observed
179 // by any ReportingObservers.
180 auto* body = MakeGarbageCollected<CSPViolationReportBody>(violation_data);
181 Report* observed_report = MakeGarbageCollected<Report>(
182 ReportType::kCSPViolation, Url().GetString(), body);
183 ReportingContext::From(document->ToExecutionContext())
184 ->QueueReport(observed_report,
185 use_reporting_api ? report_endpoints : Vector<String>());
186
187 if (use_reporting_api)
188 return;
189
190 for (const auto& report_endpoint : report_endpoints) {
191 // Use the frame's document to complete the endpoint URL, overriding its URL
192 // with the blocked document's URL.
193 // https://w3c.github.io/webappsec-csp/#report-violation
194 // Step 3.4.2.1. Let endpoint be the result of executing the URL parser with
195 // token as the input, and violation’s url as the base URL. [spec text]
196 KURL url = is_frame_ancestors_violation
197 ? document->CompleteURLWithOverride(
198 report_endpoint, KURL(violation_data.blockedURI()))
199 // We use the FallbackBaseURL to ensure that we don't
200 // respect base elements when determining the report
201 // endpoint URL.
202 // Note: According to Step 3.4.2.1 mentioned above, the base
203 // URL is "violation’s url" which should be violation's
204 // global object's URL. So using FallbackBaseURL() might be
205 // inconsistent.
206 : document->CompleteURLWithOverride(
207 report_endpoint, document->FallbackBaseURL());
208 PingLoader::SendViolationReport(frame, url, report);
209 }
210 }
211
Count(WebFeature feature)212 void ExecutionContextCSPDelegate::Count(WebFeature feature) {
213 UseCounter::Count(execution_context_, feature);
214 }
215
AddConsoleMessage(ConsoleMessage * console_message)216 void ExecutionContextCSPDelegate::AddConsoleMessage(
217 ConsoleMessage* console_message) {
218 execution_context_->AddConsoleMessage(console_message);
219 }
220
DisableEval(const String & error_message)221 void ExecutionContextCSPDelegate::DisableEval(const String& error_message) {
222 execution_context_->DisableEval(error_message);
223 }
224
ReportBlockedScriptExecutionToInspector(const String & directive_text)225 void ExecutionContextCSPDelegate::ReportBlockedScriptExecutionToInspector(
226 const String& directive_text) {
227 probe::ScriptExecutionBlockedByCSP(execution_context_, directive_text);
228 }
229
DidAddContentSecurityPolicies(WTF::Vector<network::mojom::blink::ContentSecurityPolicyPtr> policies)230 void ExecutionContextCSPDelegate::DidAddContentSecurityPolicies(
231 WTF::Vector<network::mojom::blink::ContentSecurityPolicyPtr> policies) {
232 Document* document = GetDocument();
233 if (!document)
234 return;
235
236 LocalFrame* frame = document->GetFrame();
237 if (!frame)
238 return;
239
240 frame->GetLocalFrameHostRemote().DidAddContentSecurityPolicies(
241 std::move(policies));
242 }
243
GetSecurityContext()244 SecurityContext& ExecutionContextCSPDelegate::GetSecurityContext() {
245 return execution_context_->GetSecurityContext();
246 }
247
GetDocument()248 Document* ExecutionContextCSPDelegate::GetDocument() {
249 auto* window = DynamicTo<LocalDOMWindow>(execution_context_.Get());
250 return window ? window->document() : nullptr;
251 }
252
DispatchViolationEventInternal(const SecurityPolicyViolationEventInit * violation_data,Element * element)253 void ExecutionContextCSPDelegate::DispatchViolationEventInternal(
254 const SecurityPolicyViolationEventInit* violation_data,
255 Element* element) {
256 // Worklets don't support Events in general.
257 if (execution_context_->IsWorkletGlobalScope())
258 return;
259
260 // https://w3c.github.io/webappsec-csp/#report-violation.
261 // Step 3.1. If target is not null, and global is a Window, and target’s
262 // shadow-including root is not global’s associated Document, set target to
263 // null. [spec text]
264 // Step 3.2. If target is null:
265 // Step 3.2.1. Set target be violation’s global object.
266 // Step 3.2.2. If target is a Window, set target to target’s associated
267 // Document. [spec text]
268 // Step 3.3. Fire an event named securitypolicyviolation that uses the
269 // SecurityPolicyViolationEvent interface at target.. [spec text]
270 SecurityPolicyViolationEvent& event = *SecurityPolicyViolationEvent::Create(
271 event_type_names::kSecuritypolicyviolation, violation_data);
272 DCHECK(event.bubbles());
273
274 if (auto* document = GetDocument()) {
275 if (element && element->isConnected() && element->GetDocument() == document)
276 element->EnqueueEvent(event, TaskType::kInternalDefault);
277 else
278 document->EnqueueEvent(event, TaskType::kInternalDefault);
279 } else if (auto* scope = DynamicTo<WorkerGlobalScope>(*execution_context_)) {
280 scope->EnqueueEvent(event, TaskType::kInternalDefault);
281 }
282 }
283
284 } // namespace blink
285