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/trustedtypes/trusted_types_util.h"
6 
7 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink-forward.h"
8 #include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h"
9 #include "third_party/blink/public/platform/platform.h"
10 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
11 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_html_or_trusted_script_or_trusted_script_url.h"
12 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
13 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
14 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
15 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
16 #include "third_party/blink/renderer/core/inspector/console_message.h"
17 #include "third_party/blink/renderer/core/script/script_element_base.h"
18 #include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
19 #include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
20 #include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
21 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h"
22 #include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
23 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
24 #include "third_party/blink/renderer/platform/bindings/script_state.h"
25 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
26 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
27 
28 namespace blink {
29 
30 namespace {
31 
32 enum TrustedTypeViolationKind {
33   kTrustedHTMLAssignment,
34   kTrustedScriptAssignment,
35   kTrustedScriptURLAssignment,
36   kTrustedHTMLAssignmentAndDefaultPolicyFailed,
37   kTrustedHTMLAssignmentAndNoDefaultPolicyExisted,
38   kTrustedScriptAssignmentAndDefaultPolicyFailed,
39   kTrustedScriptAssignmentAndNoDefaultPolicyExisted,
40   kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
41   kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted,
42   kNavigateToJavascriptURL,
43   kNavigateToJavascriptURLAndDefaultPolicyFailed,
44   kScriptExecution,
45   kScriptExecutionAndDefaultPolicyFailed,
46 };
47 
48 const char kFunctionConstructorFailureConsoleMessage[] =
49     "The JavaScript Function constructor does not accept TrustedString "
50     "arguments. See https://github.com/w3c/webappsec-trusted-types/wiki/"
51     "Trusted-Types-for-function-constructor for more information.";
52 
GetMessage(TrustedTypeViolationKind kind)53 const char* GetMessage(TrustedTypeViolationKind kind) {
54   switch (kind) {
55     case kTrustedHTMLAssignment:
56       return "This document requires 'TrustedHTML' assignment.";
57     case kTrustedScriptAssignment:
58       return "This document requires 'TrustedScript' assignment.";
59     case kTrustedScriptURLAssignment:
60       return "This document requires 'TrustedScriptURL' assignment.";
61     case kTrustedHTMLAssignmentAndDefaultPolicyFailed:
62       return "This document requires 'TrustedHTML' assignment and the "
63              "'default' policy failed to execute.";
64     case kTrustedHTMLAssignmentAndNoDefaultPolicyExisted:
65       return "This document requires 'TrustedHTML' assignment and no "
66              "'default' policy for 'TrustedHTML' has been defined.";
67     case kTrustedScriptAssignmentAndDefaultPolicyFailed:
68       return "This document requires 'TrustedScript' assignment and the "
69              "'default' policy failed to execute.";
70     case kTrustedScriptAssignmentAndNoDefaultPolicyExisted:
71       return "This document requires 'TrustedScript' assignment and no "
72              "'default' policy for 'TrustedScript' has been defined.";
73     case kTrustedScriptURLAssignmentAndDefaultPolicyFailed:
74       return "This document requires 'TrustedScriptURL' assignment and the "
75              "'default' policy failed to execute.";
76     case kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted:
77       return "This document requires 'TrustedScriptURL' assignment and no "
78              "'default' policy for 'TrustedScriptURL' has been defined.";
79     case kNavigateToJavascriptURL:
80       return "This document requires 'TrustedScript' assignment. "
81              "Navigating to a javascript:-URL is equivalent to a "
82              "'TrustedScript' assignment.";
83     case kNavigateToJavascriptURLAndDefaultPolicyFailed:
84       return "This document requires 'TrustedScript' assignment. "
85              "Navigating to a javascript:-URL is equivalent to a "
86              "'TrustedScript' assignment and the 'default' policy failed to"
87              "execute.";
88     case kScriptExecution:
89       return "This document requires 'TrustedScript' assignment. "
90              "This script element was modified without use of TrustedScript "
91              "assignment.";
92     case kScriptExecutionAndDefaultPolicyFailed:
93       return "This document requires 'TrustedScript' assignment. "
94              "This script element was modified without use of TrustedScript "
95              "assignment and the 'default' policy failed to execute.";
96   }
97   NOTREACHED();
98   return "";
99 }
100 
GetSamplePrefix(const ExceptionState & exception_state)101 String GetSamplePrefix(const ExceptionState& exception_state) {
102   const char* interface_name = exception_state.InterfaceName();
103   const char* property_name = exception_state.PropertyName();
104 
105   // We have two sample formats, one for eval and one for assignment.
106   // If we don't have the required values being passed in, just leave the
107   // sample empty.
108   StringBuilder sample_prefix;
109   if (!interface_name) {
110     // No interface name? Then we have no prefix to use.
111   } else if (strcmp("eval", interface_name) == 0) {
112     sample_prefix.Append("eval");
113   } else if ((strcmp("Worker", interface_name) == 0 ||
114               strcmp("SharedWorker", interface_name) == 0) &&
115              !property_name) {
116     // Worker/SharedWorker constructor has nullptr as property_name.
117     sample_prefix.Append(interface_name);
118     sample_prefix.Append(" constructor");
119   } else if (interface_name && property_name) {
120     sample_prefix.Append(interface_name);
121     sample_prefix.Append(" ");
122     sample_prefix.Append(property_name);
123   }
124   return sample_prefix.ToString();
125 }
126 
GetElementName(const ScriptElementBase::Type type)127 const char* GetElementName(const ScriptElementBase::Type type) {
128   switch (type) {
129     case ScriptElementBase::Type::kHTMLScriptElement:
130       return "HTMLScriptElement";
131     case ScriptElementBase::Type::kSVGScriptElement:
132       return "SVGScriptElement";
133   }
134   NOTREACHED();
135   return "";
136 }
137 
GetDefaultCallbackArgs(v8::Isolate * isolate,const char * type,const ExceptionState & exception_state)138 HeapVector<ScriptValue> GetDefaultCallbackArgs(
139     v8::Isolate* isolate,
140     const char* type,
141     const ExceptionState& exception_state) {
142   ScriptState* script_state = ScriptState::Current(isolate);
143   HeapVector<ScriptValue> args;
144   args.push_back(ScriptValue::From(script_state, type));
145   args.push_back(
146       ScriptValue::From(script_state, GetSamplePrefix(exception_state)));
147   return args;
148 }
149 
150 // Handle failure of a Trusted Type assignment.
151 //
152 // If trusted type assignment fails, we need to
153 // - report the violation via CSP
154 // - increment the appropriate counter,
155 // - raise a JavaScript exception (if enforced).
156 //
157 // Returns whether the failure should be enforced.
TrustedTypeFail(TrustedTypeViolationKind kind,const ExecutionContext * execution_context,ExceptionState & exception_state,const String & value)158 bool TrustedTypeFail(TrustedTypeViolationKind kind,
159                      const ExecutionContext* execution_context,
160                      ExceptionState& exception_state,
161                      const String& value) {
162   if (!execution_context)
163     return true;
164 
165   // Test case docs (Document::CreateForTest()) might not have a window
166   // and hence no TrustedTypesPolicyFactory.
167   if (execution_context->GetTrustedTypes())
168     execution_context->GetTrustedTypes()->CountTrustedTypeAssignmentError();
169 
170   const char* kAnonymousPrefix = "(function anonymous";
171   String prefix = GetSamplePrefix(exception_state);
172   if (prefix == "eval" && value.StartsWith(kAnonymousPrefix)) {
173     prefix = "Function";
174   }
175   bool allow =
176       execution_context->GetContentSecurityPolicy()
177           ->AllowTrustedTypeAssignmentFailure(
178               GetMessage(kind),
179               prefix == "Function" ? value.Substring(strlen(kAnonymousPrefix))
180                                    : value,
181               prefix);
182 
183   // TODO(1087743): Add a console message for Trusted Type-related Function
184   // constructor failures, to warn the developer of the outstanding issues
185   // with TT and Function  constructors. This should be removed once the
186   // underlying issue has been fixed.
187   if (prefix == "Function" && !allow) {
188     DCHECK(kind == kTrustedScriptAssignment ||
189            kind == kTrustedScriptAssignmentAndDefaultPolicyFailed ||
190            kind == kTrustedScriptAssignmentAndNoDefaultPolicyExisted);
191     execution_context->GetContentSecurityPolicy()->LogToConsole(
192         MakeGarbageCollected<ConsoleMessage>(
193             mojom::blink::ConsoleMessageSource::kRecommendation,
194             mojom::blink::ConsoleMessageLevel::kInfo,
195             kFunctionConstructorFailureConsoleMessage));
196   }
197 
198   if (!allow) {
199     exception_state.ThrowTypeError(GetMessage(kind));
200   }
201   return !allow;
202 }
203 
GetDefaultPolicy(const ExecutionContext * execution_context)204 TrustedTypePolicy* GetDefaultPolicy(const ExecutionContext* execution_context) {
205   DCHECK(execution_context);
206   return execution_context->GetTrustedTypes()
207              ? execution_context->GetTrustedTypes()->defaultPolicy()
208              : nullptr;
209 }
210 
211 // Functionally identical to TrustedTypesCheckForScript(const String&, ..), but
212 // to be called outside of regular script execution. This is required for both
213 // GetStringForScriptExecution & TrustedTypesCheckForJavascriptURLinNavigation,
214 // and has a number of additional parameters to enable proper error reporting
215 // for each case.
GetStringFromScriptHelper(String script,ExecutionContext * context,const char * element_name_for_exception,const char * attribute_name_for_exception,TrustedTypeViolationKind violation_kind,TrustedTypeViolationKind violation_kind_when_default_policy_failed)216 String GetStringFromScriptHelper(
217     String script,
218     ExecutionContext* context,
219 
220     // Parameters to customize error messages:
221     const char* element_name_for_exception,
222     const char* attribute_name_for_exception,
223     TrustedTypeViolationKind violation_kind,
224     TrustedTypeViolationKind violation_kind_when_default_policy_failed) {
225   if (!context)
226     return script;
227   if (!RequireTrustedTypesCheck(context))
228     return script;
229 
230   // Set up JS context & friends.
231   //
232   // All other functions in here are expected to be called during JS execution,
233   // where naturally everything is properly set up for more JS execution.
234   // This one is called during navigation, and thus needs to do a bit more
235   // work. We need two JavaScript-ish things:
236   // - TrustedTypeFail expects an ExceptionState, which it will use to throw
237   //   an exception. In our case, we will always clear the exception (as there
238   //   is no user script to pass it to), and we only use this as a signalling
239   //   mechanism.
240   // - If the default policy applies, we need to execute the JS callback.
241   //   Unlike the various ScriptController::Execute* and ..::Eval* methods,
242   //   we are not executing a source String, but an already compiled callback
243   //   function.
244   v8::HandleScope handle_scope(context->GetIsolate());
245   ScriptState::Scope script_state_scope(
246       ToScriptState(context, DOMWrapperWorld::MainWorld()));
247   ExceptionState exception_state(
248       context->GetIsolate(), ExceptionState::kUnknownContext,
249       element_name_for_exception, attribute_name_for_exception);
250 
251   TrustedTypePolicy* default_policy = GetDefaultPolicy(context);
252   if (!default_policy) {
253     if (TrustedTypeFail(violation_kind, context, exception_state, script)) {
254       exception_state.ClearException();
255       return String();
256     }
257     return script;
258   }
259 
260   TrustedScript* result = default_policy->CreateScript(
261       context->GetIsolate(), script,
262       GetDefaultCallbackArgs(context->GetIsolate(), "TrustedScript",
263                              exception_state),
264       exception_state);
265   if (exception_state.HadException()) {
266     exception_state.ClearException();
267     return String();
268   }
269 
270   if (result->toString().IsNull()) {
271     if (TrustedTypeFail(violation_kind_when_default_policy_failed, context,
272                         exception_state, script)) {
273       exception_state.ClearException();
274       return String();
275     }
276     return script;
277   }
278   return result->toString();
279 }
280 
281 }  // namespace
282 
RequireTrustedTypesCheck(const ExecutionContext * execution_context)283 bool RequireTrustedTypesCheck(const ExecutionContext* execution_context) {
284   return execution_context && execution_context->RequireTrustedTypes() &&
285          !ContentSecurityPolicy::ShouldBypassMainWorld(execution_context);
286 }
287 
TrustedTypesCheckForHTML(String html,const ExecutionContext * execution_context,ExceptionState & exception_state)288 String TrustedTypesCheckForHTML(String html,
289                                 const ExecutionContext* execution_context,
290                                 ExceptionState& exception_state) {
291   bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
292   if (!require_trusted_type) {
293     return html;
294   }
295 
296   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
297   if (!default_policy) {
298     if (TrustedTypeFail(kTrustedHTMLAssignment, execution_context,
299                         exception_state, html)) {
300       return g_empty_string;
301     }
302     return html;
303   }
304 
305   if (!default_policy->HasCreateHTML()) {
306     if (TrustedTypeFail(kTrustedHTMLAssignmentAndNoDefaultPolicyExisted,
307                         execution_context, exception_state, html)) {
308       return g_empty_string;
309     } else {
310       return html;
311     }
312   }
313   // TODO(ajwong): This can be optimized to avoid a AddRef in the
314   // StringCache::CreateStringAndInsertIntoCache() also, but it's a hard mess.
315   // Punt for now.
316   TrustedHTML* result = default_policy->CreateHTML(
317       execution_context->GetIsolate(), html,
318       GetDefaultCallbackArgs(execution_context->GetIsolate(), "TrustedHTML",
319                              exception_state),
320       exception_state);
321   if (exception_state.HadException()) {
322     return g_empty_string;
323   }
324 
325   if (result->toString().IsNull()) {
326     if (TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
327                         execution_context, exception_state, html)) {
328       return g_empty_string;
329     } else {
330       return html;
331     }
332   }
333 
334   return result->toString();
335 }
336 
TrustedTypesCheckForScript(String script,const ExecutionContext * execution_context,ExceptionState & exception_state)337 String TrustedTypesCheckForScript(String script,
338                                   const ExecutionContext* execution_context,
339                                   ExceptionState& exception_state) {
340   bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
341   if (!require_trusted_type) {
342     return script;
343   }
344 
345   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
346   if (!default_policy) {
347     if (TrustedTypeFail(kTrustedScriptAssignment, execution_context,
348                         exception_state, script)) {
349       return g_empty_string;
350     }
351     return script;
352   }
353 
354   if (!default_policy->HasCreateScript()) {
355     if (TrustedTypeFail(kTrustedScriptAssignmentAndNoDefaultPolicyExisted,
356                         execution_context, exception_state, script)) {
357       return g_empty_string;
358     } else {
359       return script;
360     }
361   }
362   // TODO(ajwong): This can be optimized to avoid a AddRef in the
363   // StringCache::CreateStringAndInsertIntoCache() also, but it's a hard mess.
364   // Punt for now.
365   TrustedScript* result = default_policy->CreateScript(
366       execution_context->GetIsolate(), script,
367       GetDefaultCallbackArgs(execution_context->GetIsolate(), "TrustedScript",
368                              exception_state),
369       exception_state);
370   DCHECK_EQ(!result, exception_state.HadException());
371   if (exception_state.HadException()) {
372     return g_empty_string;
373   }
374 
375   if (result->toString().IsNull()) {
376     if (TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
377                         execution_context, exception_state, script)) {
378       return g_empty_string;
379     } else {
380       return script;
381     }
382   }
383 
384   return result->toString();
385 }
386 
TrustedTypesCheckForScriptURL(String script_url,const ExecutionContext * execution_context,ExceptionState & exception_state)387 String TrustedTypesCheckForScriptURL(String script_url,
388                                      const ExecutionContext* execution_context,
389                                      ExceptionState& exception_state) {
390   bool require_trusted_type =
391       RequireTrustedTypesCheck(execution_context) &&
392       RuntimeEnabledFeatures::TrustedDOMTypesEnabled(execution_context);
393   if (!require_trusted_type) {
394     return script_url;
395   }
396 
397   TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
398   if (!default_policy) {
399     if (TrustedTypeFail(kTrustedScriptURLAssignment, execution_context,
400                         exception_state, script_url)) {
401       return g_empty_string;
402     }
403     return script_url;
404   }
405 
406   if (!default_policy->HasCreateScriptURL()) {
407     if (TrustedTypeFail(kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted,
408                         execution_context, exception_state, script_url)) {
409       return g_empty_string;
410     } else {
411       return script_url;
412     }
413   }
414   // TODO(ajwong): This can be optimized to avoid a AddRef in the
415   // StringCache::CreateStringAndInsertIntoCache() also, but it's a hard mess.
416   // Punt for now.
417   TrustedScriptURL* result = default_policy->CreateScriptURL(
418       execution_context->GetIsolate(), script_url,
419       GetDefaultCallbackArgs(execution_context->GetIsolate(),
420                              "TrustedScriptURL", exception_state),
421       exception_state);
422 
423   if (exception_state.HadException()) {
424     return g_empty_string;
425   }
426 
427   if (result->toString().IsNull()) {
428     if (TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
429                         execution_context, exception_state, script_url)) {
430       return g_empty_string;
431     } else {
432       return script_url;
433     }
434   }
435 
436   return result->toString();
437 }
438 
TrustedTypesCheckFor(SpecificTrustedType type,const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL & trusted,const ExecutionContext * execution_context,ExceptionState & exception_state)439 String TrustedTypesCheckFor(
440     SpecificTrustedType type,
441     const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL& trusted,
442     const ExecutionContext* execution_context,
443     ExceptionState& exception_state) {
444   // Whatever happens below, we will need the string value:
445   String value;
446   if (trusted.IsTrustedHTML()) {
447     value = trusted.GetAsTrustedHTML()->toString();
448   } else if (trusted.IsTrustedScript()) {
449     value = trusted.GetAsTrustedScript()->toString();
450   } else if (trusted.IsTrustedScriptURL()) {
451     value = trusted.GetAsTrustedScriptURL()->toString();
452   } else if (trusted.IsString()) {
453     value = trusted.GetAsString();
454   }  // else: trusted.IsNull(). But we don't have anything to do in that case.
455 
456   // The check passes if we have the proper trusted type:
457   if (type == SpecificTrustedType::kNone ||
458       (trusted.IsTrustedHTML() && type == SpecificTrustedType::kHTML) ||
459       (trusted.IsTrustedScript() && type == SpecificTrustedType::kScript) ||
460       (trusted.IsTrustedScriptURL() &&
461        type == SpecificTrustedType::kScriptURL)) {
462     return value;
463   }
464 
465   // In all other cases: run the full check against the string value.
466   return TrustedTypesCheckFor(type, std::move(value), execution_context,
467                               exception_state);
468 }
469 
TrustedTypesCheckForScript(StringOrTrustedScript trusted,const ExecutionContext * execution_context,ExceptionState & exception_state)470 String TrustedTypesCheckForScript(StringOrTrustedScript trusted,
471                                   const ExecutionContext* execution_context,
472                                   ExceptionState& exception_state) {
473   // To remain compatible with legacy behaviour, HTMLElement uses extended IDL
474   // attributes to allow for nullable union of (DOMString or TrustedScript).
475   // Thus, this method is required to handle the case where
476   // string_or_trusted_script.IsNull(), unlike the various similar methods in
477   // this file.
478   if (trusted.IsTrustedScript()) {
479     return trusted.GetAsTrustedScript()->toString();
480   }
481   if (trusted.IsNull()) {
482     trusted = StringOrTrustedScript::FromString(g_empty_string);
483   }
484   return TrustedTypesCheckForScript(trusted.GetAsString(), execution_context,
485                                     exception_state);
486 }
487 
TrustedTypesCheckFor(SpecificTrustedType type,String trusted,const ExecutionContext * execution_context,ExceptionState & exception_state)488 String TrustedTypesCheckFor(SpecificTrustedType type,
489                             String trusted,
490                             const ExecutionContext* execution_context,
491                             ExceptionState& exception_state) {
492   switch (type) {
493     case SpecificTrustedType::kHTML:
494       return TrustedTypesCheckForHTML(std::move(trusted), execution_context,
495                                       exception_state);
496     case SpecificTrustedType::kScript:
497       return TrustedTypesCheckForScript(std::move(trusted), execution_context,
498                                         exception_state);
499     case SpecificTrustedType::kScriptURL:
500       return TrustedTypesCheckForScriptURL(std::move(trusted),
501                                            execution_context, exception_state);
502     case SpecificTrustedType::kNone:
503       return trusted;
504   }
505   NOTREACHED();
506   return g_empty_string;
507 }
508 
509 String CORE_EXPORT
GetStringForScriptExecution(String script,const ScriptElementBase::Type type,ExecutionContext * context)510 GetStringForScriptExecution(String script,
511                             const ScriptElementBase::Type type,
512                             ExecutionContext* context) {
513   return GetStringFromScriptHelper(
514       std::move(script), context, GetElementName(type), "text",
515       kScriptExecution, kScriptExecutionAndDefaultPolicyFailed);
516 }
517 
TrustedTypesCheckForJavascriptURLinNavigation(String javascript_url,ExecutionContext * context)518 String TrustedTypesCheckForJavascriptURLinNavigation(
519     String javascript_url,
520     ExecutionContext* context) {
521   return GetStringFromScriptHelper(
522       std::move(javascript_url), context, "Location", "href",
523       kNavigateToJavascriptURL, kNavigateToJavascriptURLAndDefaultPolicyFailed);
524 }
525 
526 }  // namespace blink
527