Skip to content

Commit fb2062a

Browse files
tete17gmta
authored andcommitted
LibWeb: Allow CSP to block eval invocations based on TrustedTypes
This allows trusted types directives to also restrict the contents passed to eval.
1 parent 55afa9d commit fb2062a

File tree

2 files changed

+79
-9
lines changed

2 files changed

+79
-9
lines changed

Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.cpp

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <LibWeb/HTML/WorkerGlobalScope.h>
2121
#include <LibWeb/Infra/Strings.h>
2222
#include <LibWeb/SRI/SRI.h>
23+
#include <LibWeb/TrustedTypes/RequireTrustedTypesForDirective.h>
24+
#include <LibWeb/TrustedTypes/TrustedTypePolicy.h>
2325
#include <LibWeb/WebAssembly/WebAssembly.h>
2426

2527
namespace Web::ContentSecurityPolicy {
@@ -461,13 +463,81 @@ Directives::Directive::Result should_elements_inline_type_behavior_be_blocked_by
461463
}
462464

463465
// https://w3c.github.io/webappsec-csp/#can-compile-strings
464-
JS::ThrowCompletionOr<void> ensure_csp_does_not_block_string_compilation(JS::Realm& realm, ReadonlySpan<String>, StringView, StringView code_string, JS::CompilationType, ReadonlySpan<JS::Value>, JS::Value)
466+
JS::ThrowCompletionOr<void> ensure_csp_does_not_block_string_compilation(JS::Realm& realm, ReadonlySpan<String> parameter_strings, StringView body_string, StringView code_string, JS::CompilationType compilation_type, ReadonlySpan<JS::Value> parameter_args, JS::Value body_arg)
465467
{
466-
// FIXME: 1. If compilationType is "TIMER", then:
467-
// 1. Let sourceString be codeString.
468-
StringView source_string = code_string;
469-
// FIXME: 2. Else:
470-
// FIXME: We don't do these two steps as we don't currently support Trusted Types.
468+
Utf16String source_string;
469+
470+
// 1. If compilationType is "TIMER", then:
471+
if (compilation_type == JS::CompilationType::Timer) {
472+
// 1. Let sourceString be codeString.
473+
source_string = Utf16String::from_utf8(code_string);
474+
}
475+
// 2. Else:
476+
else {
477+
// 1. Let compilationSink be "Function" if compilationType is "FUNCTION", and "eval" otherwise.
478+
auto const compilation_sink = compilation_type == JS::CompilationType::Function ? TrustedTypes::InjectionSink::Function : TrustedTypes::InjectionSink::Eval;
479+
480+
// 2. Let isTrusted be true if bodyArg implements TrustedScript, and false otherwise.
481+
auto is_trusted = body_arg.is_object() && is<TrustedTypes::TrustedScript>(body_arg.as_object());
482+
483+
// 3. If isTrusted is true then:
484+
if (is_trusted) {
485+
// 1. If bodyString is not equal to bodyArg’s data, set isTrusted to false.
486+
if (body_string != as<TrustedTypes::TrustedScript>(body_arg.as_object()).to_string())
487+
is_trusted = false;
488+
}
489+
490+
// 4. If isTrusted is true, then:
491+
if (is_trusted) {
492+
// 1. Assert: parameterArgs’ [list/size=] is equal to [parameterStrings]' size.
493+
VERIFY(parameter_args.size() == parameter_strings.size());
494+
495+
// 2. For each index of the range 0 to |parameterArgs]' [list/size=]:
496+
for (size_t i = 0; i < parameter_args.size(); i++) {
497+
// 1. Let arg be parameterArgs[index].
498+
auto const& arg = parameter_args[i];
499+
500+
// 2. If arg implements TrustedScript, then:
501+
if (arg.is_object() && is<TrustedTypes::TrustedScript>(arg.as_object())) {
502+
// 1. if parameterStrings[index] is not equal to arg’s data, set isTrusted to false.
503+
if (parameter_strings[i] != as<TrustedTypes::TrustedScript>(arg.as_object()).to_string()) {
504+
is_trusted = false;
505+
break;
506+
}
507+
}
508+
// 3. Otherwise, set isTrusted to false.
509+
else {
510+
is_trusted = false;
511+
break;
512+
}
513+
}
514+
}
515+
516+
// 5. Let sourceToValidate be a new TrustedScript object created in realm whose data is set to codeString
517+
// if isTrusted is true, and codeString otherwise.
518+
auto const source_to_validate = is_trusted
519+
? TrustedTypes::TrustedScriptOrString(realm.create<TrustedTypes::TrustedScript>(realm, Utf16String::from_utf8(code_string)))
520+
: Utf16String::from_utf8(code_string);
521+
522+
// 6. Let sourceString be the result of executing the Get Trusted Type compliant string algorithm,
523+
// with TrustedScript, realm, sourceToValidate, compilationSink, and 'script'.
524+
auto maybe_source_string = TrustedTypes::get_trusted_type_compliant_string(
525+
TrustedTypes::TrustedTypeName::TrustedScript,
526+
realm.global_object(),
527+
source_to_validate,
528+
compilation_sink,
529+
TrustedTypes::Script.to_string());
530+
531+
// 7. If the algorithm throws an error, throw an EvalError.
532+
if (maybe_source_string.is_error()) {
533+
return realm.vm().throw_completion<JS::EvalError>("Blocked by Content Security Policy"sv);
534+
}
535+
source_string = maybe_source_string.release_value();
536+
537+
// 8. If sourceString is not equal to codeString, throw an EvalError.
538+
if (source_string != code_string)
539+
return realm.vm().throw_completion<JS::EvalError>("Blocked by Content Security Policy"sv);
540+
}
471541

472542
// 3. Let result be "Allowed".
473543
auto result = Directives::Directive::Result::Allowed;
@@ -526,9 +596,8 @@ JS::ThrowCompletionOr<void> ensure_csp_does_not_block_string_compilation(JS::Rea
526596
});
527597

528598
if (!maybe_report_sample.is_end()) {
529-
Utf8View source_view { source_string };
530-
auto sample = source_view.unicode_substring_view(0, min(source_view.length(), 40));
531-
violation->set_sample(String::from_utf8_without_validation(sample.as_string().bytes()));
599+
auto source_view = source_string.substring_view(0, min(source_string.length_in_code_units(), 40));
600+
violation->set_sample(source_view.to_utf8_but_should_be_ported_to_utf16());
532601
}
533602

534603
// 4. Execute § 5.5 Report a violation on violation.

Libraries/LibWeb/TrustedTypes/InjectionSink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Web::TrustedTypes {
2626
__ENUMERATE_INJECTION_SINKS(Element_insertAdjacentHTML, "Element insertAdjacentHTML") \
2727
__ENUMERATE_INJECTION_SINKS(Element_outerHTML, "Element outerHTML") \
2828
__ENUMERATE_INJECTION_SINKS(Element_setHTMLUnsafe, "Element setHTMLUnsafe") \
29+
__ENUMERATE_INJECTION_SINKS(Eval, "eval") \
2930
__ENUMERATE_INJECTION_SINKS(Function, "Function") \
3031
__ENUMERATE_INJECTION_SINKS(HTMLIFrameElement_srcdoc, "HTMLIFrameElement srcdoc") \
3132
__ENUMERATE_INJECTION_SINKS(HTMLScriptElement_innerText, "HTMLScriptElement innerText") \

0 commit comments

Comments
 (0)