|
20 | 20 | #include <LibWeb/HTML/WorkerGlobalScope.h> |
21 | 21 | #include <LibWeb/Infra/Strings.h> |
22 | 22 | #include <LibWeb/SRI/SRI.h> |
| 23 | +#include <LibWeb/TrustedTypes/RequireTrustedTypesForDirective.h> |
| 24 | +#include <LibWeb/TrustedTypes/TrustedTypePolicy.h> |
23 | 25 | #include <LibWeb/WebAssembly/WebAssembly.h> |
24 | 26 |
|
25 | 27 | namespace Web::ContentSecurityPolicy { |
@@ -461,13 +463,81 @@ Directives::Directive::Result should_elements_inline_type_behavior_be_blocked_by |
461 | 463 | } |
462 | 464 |
|
463 | 465 | // 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) |
465 | 467 | { |
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 | + } |
471 | 541 |
|
472 | 542 | // 3. Let result be "Allowed". |
473 | 543 | auto result = Directives::Directive::Result::Allowed; |
@@ -526,9 +596,8 @@ JS::ThrowCompletionOr<void> ensure_csp_does_not_block_string_compilation(JS::Rea |
526 | 596 | }); |
527 | 597 |
|
528 | 598 | 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()); |
532 | 601 | } |
533 | 602 |
|
534 | 603 | // 4. Execute § 5.5 Report a violation on violation. |
|
0 commit comments