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