blob: 0858ac2a1f15b76a3ad7e04219e19ae1dcea2cf0 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
#include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_html_or_trusted_script_or_trusted_script_url.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/script/script_element_base.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_html.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_script_url.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h"
#include "third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
namespace {
enum TrustedTypeViolationKind {
kTrustedHTMLAssignment,
kTrustedScriptAssignment,
kTrustedScriptURLAssignment,
kTrustedHTMLAssignmentAndDefaultPolicyFailed,
kTrustedHTMLAssignmentAndNoDefaultPolicyExisted,
kTrustedScriptAssignmentAndDefaultPolicyFailed,
kTrustedScriptAssignmentAndNoDefaultPolicyExisted,
kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted,
kNavigateToJavascriptURL,
kNavigateToJavascriptURLAndDefaultPolicyFailed,
kScriptExecution,
kScriptExecutionAndDefaultPolicyFailed,
};
const char* GetMessage(TrustedTypeViolationKind kind) {
switch (kind) {
case kTrustedHTMLAssignment:
return "This document requires 'TrustedHTML' assignment.";
case kTrustedScriptAssignment:
return "This document requires 'TrustedScript' assignment.";
case kTrustedScriptURLAssignment:
return "This document requires 'TrustedScriptURL' assignment.";
case kTrustedHTMLAssignmentAndDefaultPolicyFailed:
return "This document requires 'TrustedHTML' assignment and the "
"'default' policy failed to execute.";
case kTrustedHTMLAssignmentAndNoDefaultPolicyExisted:
return "This document requires 'TrustedHTML' assignment and no "
"'default' policy for 'TrustedHTML' has been defined.";
case kTrustedScriptAssignmentAndDefaultPolicyFailed:
return "This document requires 'TrustedScript' assignment and the "
"'default' policy failed to execute.";
case kTrustedScriptAssignmentAndNoDefaultPolicyExisted:
return "This document requires 'TrustedScript' assignment and no "
"'default' policy for 'TrustedScript' has been defined.";
case kTrustedScriptURLAssignmentAndDefaultPolicyFailed:
return "This document requires 'TrustedScriptURL' assignment and the "
"'default' policy failed to execute.";
case kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted:
return "This document requires 'TrustedScriptURL' assignment and no "
"'default' policy for 'TrustedScriptURL' has been defined.";
case kNavigateToJavascriptURL:
return "This document requires 'TrustedScript' assignment. "
"Navigating to a javascript:-URL is equivalent to a "
"'TrustedScript' assignment.";
case kNavigateToJavascriptURLAndDefaultPolicyFailed:
return "This document requires 'TrustedScript' assignment. "
"Navigating to a javascript:-URL is equivalent to a "
"'TrustedScript' assignment and the 'default' policy failed to"
"execute.";
case kScriptExecution:
return "This document requires 'TrustedScript' assignment. "
"This script element was modified without use of TrustedScript "
"assignment.";
case kScriptExecutionAndDefaultPolicyFailed:
return "This document requires 'TrustedScript' assignment. "
"This script element was modified without use of TrustedScript "
"assignment and the 'default' policy failed to execute.";
}
NOTREACHED();
return "";
}
String GetSamplePrefix(const ExceptionState& exception_state) {
const char* interface_name = exception_state.InterfaceName();
const char* property_name = exception_state.PropertyName();
// We have two sample formats, one for eval and one for assignment.
// If we don't have the required values being passed in, just leave the
// sample empty.
StringBuilder sample_prefix;
if (interface_name && strcmp("eval", interface_name) == 0) {
sample_prefix.Append("eval");
} else if (interface_name && property_name) {
sample_prefix.Append(interface_name);
sample_prefix.Append(" ");
sample_prefix.Append(property_name);
}
return sample_prefix.ToString();
}
const char* GetElementName(const ScriptElementBase::Type type) {
switch (type) {
case ScriptElementBase::Type::kHTMLScriptElement:
return "HTMLScriptElement";
case ScriptElementBase::Type::kSVGScriptElement:
return "SVGScriptElement";
}
NOTREACHED();
return "";
}
HeapVector<ScriptValue> GetDefaultCallbackArgs(
v8::Isolate* isolate,
const char* type,
const ExceptionState& exception_state) {
ScriptState* script_state = ScriptState::Current(isolate);
HeapVector<ScriptValue> args;
args.push_back(ScriptValue::From(script_state, type));
args.push_back(
ScriptValue::From(script_state, GetSamplePrefix(exception_state)));
return args;
}
// Handle failure of a Trusted Type assignment.
//
// If trusted type assignment fails, we need to
// - report the violation via CSP
// - increment the appropriate counter,
// - raise a JavaScript exception (if enforced).
//
// Returns whether the failure should be enforced.
bool TrustedTypeFail(TrustedTypeViolationKind kind,
const ExecutionContext* execution_context,
ExceptionState& exception_state,
const String& value) {
if (!execution_context)
return true;
// Test case docs (MakeGarbageCollected<Document>()) might not have a window
// and hence no TrustedTypesPolicyFactory.
if (execution_context->GetTrustedTypes())
execution_context->GetTrustedTypes()->CountTrustedTypeAssignmentError();
bool allow =
execution_context->GetSecurityContext()
.GetContentSecurityPolicy()
->AllowTrustedTypeAssignmentFailure(GetMessage(kind), value,
GetSamplePrefix(exception_state));
if (!allow) {
exception_state.ThrowTypeError(GetMessage(kind));
}
return !allow;
}
TrustedTypePolicy* GetDefaultPolicy(const ExecutionContext* execution_context) {
DCHECK(execution_context);
return execution_context->GetTrustedTypes()
? execution_context->GetTrustedTypes()->defaultPolicy()
: nullptr;
}
// Functionally identical to TrustedTypesCheckForScript(const String&, ..), but
// to be called outside of regular script execution. This is required for both
// GetStringForScriptExecution & TrustedTypesCheckForJavascriptURLinNavigation,
// and has a number of additional parameters to enable proper error reporting
// for each case.
String GetStringFromScriptHelper(
const String& script,
ExecutionContext* context,
// Parameters to customize error messages:
const char* element_name_for_exception,
const char* attribute_name_for_exception,
TrustedTypeViolationKind violation_kind,
TrustedTypeViolationKind violation_kind_when_default_policy_failed) {
if (!context)
return script;
if (!RequireTrustedTypesCheck(context))
return script;
// Set up JS context & friends.
//
// All other functions in here are expected to be called during JS execution,
// where naturally everything is properly set up for more JS execution.
// This one is called during navigation, and thus needs to do a bit more
// work. We need two JavaScript-ish things:
// - TrustedTypeFail expects an ExceptionState, which it will use to throw
// an exception. In our case, we will always clear the exception (as there
// is no user script to pass it to), and we only use this as a signalling
// mechanism.
// - If the default policy applies, we need to execute the JS callback.
// Unlike the various ScriptController::Execute* and ..::Eval* methods,
// we are not executing a source String, but an already compiled callback
// function.
v8::HandleScope handle_scope(context->GetIsolate());
ScriptState::Scope script_state_scope(
ToScriptState(context, DOMWrapperWorld::MainWorld()));
ExceptionState exception_state(
context->GetIsolate(), ExceptionState::kUnknownContext,
element_name_for_exception, attribute_name_for_exception);
TrustedTypePolicy* default_policy = GetDefaultPolicy(context);
if (!default_policy) {
if (TrustedTypeFail(violation_kind, context, exception_state, script)) {
exception_state.ClearException();
return String();
}
return script;
}
TrustedScript* result = default_policy->CreateScript(
context->GetIsolate(), script,
GetDefaultCallbackArgs(context->GetIsolate(), "TrustedScript",
exception_state),
exception_state);
if (exception_state.HadException()) {
exception_state.ClearException();
return String();
}
if (result->toString().IsNull()) {
if (TrustedTypeFail(violation_kind_when_default_policy_failed, context,
exception_state, script)) {
exception_state.ClearException();
return String();
}
return script;
}
return result->toString();
}
} // namespace
bool RequireTrustedTypesCheck(const ExecutionContext* execution_context) {
return execution_context && execution_context->RequireTrustedTypes() &&
!ContentSecurityPolicy::ShouldBypassMainWorld(execution_context);
}
String TrustedTypesCheckForHTML(const String& html,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
if (!require_trusted_type) {
return html;
}
TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
if (!default_policy) {
if (TrustedTypeFail(kTrustedHTMLAssignment, execution_context,
exception_state, html)) {
return g_empty_string;
}
return html;
}
if (!default_policy->HasCreateHTML()) {
if (TrustedTypeFail(kTrustedHTMLAssignmentAndNoDefaultPolicyExisted,
execution_context, exception_state, html)) {
return g_empty_string;
} else {
return html;
}
}
TrustedHTML* result = default_policy->CreateHTML(
execution_context->GetIsolate(), html,
GetDefaultCallbackArgs(execution_context->GetIsolate(), "TrustedHTML",
exception_state),
exception_state);
if (exception_state.HadException()) {
return g_empty_string;
}
if (result->toString().IsNull()) {
if (TrustedTypeFail(kTrustedHTMLAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, html)) {
return g_empty_string;
} else {
return html;
}
}
return result->toString();
}
String TrustedTypesCheckForScript(const String& script,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
bool require_trusted_type = RequireTrustedTypesCheck(execution_context);
if (!require_trusted_type) {
return script;
}
TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
if (!default_policy) {
if (TrustedTypeFail(kTrustedScriptAssignment, execution_context,
exception_state, script)) {
return g_empty_string;
}
return script;
}
if (!default_policy->HasCreateScript()) {
if (TrustedTypeFail(kTrustedScriptAssignmentAndNoDefaultPolicyExisted,
execution_context, exception_state, script)) {
return g_empty_string;
} else {
return script;
}
}
TrustedScript* result = default_policy->CreateScript(
execution_context->GetIsolate(), script,
GetDefaultCallbackArgs(execution_context->GetIsolate(), "TrustedScript",
exception_state),
exception_state);
DCHECK_EQ(!result, exception_state.HadException());
if (exception_state.HadException()) {
return g_empty_string;
}
if (result->toString().IsNull()) {
if (TrustedTypeFail(kTrustedScriptAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, script)) {
return g_empty_string;
} else {
return script;
}
}
return result->toString();
}
String TrustedTypesCheckForScriptURL(const String& script_url,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
bool require_trusted_type =
RequireTrustedTypesCheck(execution_context) &&
RuntimeEnabledFeatures::TrustedDOMTypesEnabled(execution_context);
if (!require_trusted_type) {
return script_url;
}
TrustedTypePolicy* default_policy = GetDefaultPolicy(execution_context);
if (!default_policy) {
if (TrustedTypeFail(kTrustedScriptURLAssignment, execution_context,
exception_state, script_url)) {
return g_empty_string;
}
return script_url;
}
if (!default_policy->HasCreateScriptURL()) {
if (TrustedTypeFail(kTrustedScriptURLAssignmentAndNoDefaultPolicyExisted,
execution_context, exception_state, script_url)) {
return g_empty_string;
} else {
return script_url;
}
}
TrustedScriptURL* result = default_policy->CreateScriptURL(
execution_context->GetIsolate(), script_url,
GetDefaultCallbackArgs(execution_context->GetIsolate(),
"TrustedScriptURL", exception_state),
exception_state);
if (exception_state.HadException()) {
return g_empty_string;
}
if (result->toString().IsNull()) {
if (TrustedTypeFail(kTrustedScriptURLAssignmentAndDefaultPolicyFailed,
execution_context, exception_state, script_url)) {
return g_empty_string;
} else {
return script_url;
}
}
return result->toString();
}
String TrustedTypesCheckFor(
SpecificTrustedType type,
const StringOrTrustedHTMLOrTrustedScriptOrTrustedScriptURL& trusted,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
// Whatever happens below, we will need the string value:
String value;
if (trusted.IsTrustedHTML()) {
value = trusted.GetAsTrustedHTML()->toString();
} else if (trusted.IsTrustedScript()) {
value = trusted.GetAsTrustedScript()->toString();
} else if (trusted.IsTrustedScriptURL()) {
value = trusted.GetAsTrustedScriptURL()->toString();
} else if (trusted.IsString()) {
value = trusted.GetAsString();
} // else: trusted.IsNull(). But we don't have anything to do in that case.
// The check passes if we have the proper trusted type:
if (type == SpecificTrustedType::kNone ||
(trusted.IsTrustedHTML() && type == SpecificTrustedType::kHTML) ||
(trusted.IsTrustedScript() && type == SpecificTrustedType::kScript) ||
(trusted.IsTrustedScriptURL() &&
type == SpecificTrustedType::kScriptURL)) {
return value;
}
// In all other cases: run the full check against the string value.
return TrustedTypesCheckFor(type, value, execution_context, exception_state);
}
String TrustedTypesCheckForScript(StringOrTrustedScript trusted,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
// To remain compatible with legacy behaviour, HTMLElement uses extended IDL
// attributes to allow for nullable union of (DOMString or TrustedScript).
// Thus, this method is required to handle the case where
// string_or_trusted_script.IsNull(), unlike the various similar methods in
// this file.
if (trusted.IsTrustedScript()) {
return trusted.GetAsTrustedScript()->toString();
}
if (trusted.IsNull()) {
trusted = StringOrTrustedScript::FromString(g_empty_string);
}
return TrustedTypesCheckForScript(trusted.GetAsString(), execution_context,
exception_state);
}
String TrustedTypesCheckFor(SpecificTrustedType type,
const String& trusted,
const ExecutionContext* execution_context,
ExceptionState& exception_state) {
switch (type) {
case SpecificTrustedType::kHTML:
return TrustedTypesCheckForHTML(trusted, execution_context,
exception_state);
case SpecificTrustedType::kScript:
return TrustedTypesCheckForScript(trusted, execution_context,
exception_state);
case SpecificTrustedType::kScriptURL:
return TrustedTypesCheckForScriptURL(trusted, execution_context,
exception_state);
case SpecificTrustedType::kNone:
return trusted;
}
NOTREACHED();
return "";
}
String CORE_EXPORT
GetStringForScriptExecution(const String& script,
const ScriptElementBase::Type type,
ExecutionContext* context) {
return GetStringFromScriptHelper(script, context, GetElementName(type),
"text", kScriptExecution,
kScriptExecutionAndDefaultPolicyFailed);
}
String TrustedTypesCheckForJavascriptURLinNavigation(
const String& javascript_url,
ExecutionContext* context) {
return GetStringFromScriptHelper(
javascript_url, context, "Location", "href", kNavigateToJavascriptURL,
kNavigateToJavascriptURLAndDefaultPolicyFailed);
}
} // namespace blink