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