2024-11-25 17:29:27 +00:00
/*
* Copyright ( c ) 2025 , Luke Wilde < luke @ ladybird . org >
2025-08-07 22:41:54 +02:00
* Copyright ( c ) 2025 , Kenneth Myhra < kennethmyhra @ ladybird . org >
2024-11-25 17:29:27 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibWeb/ContentSecurityPolicy/BlockingAlgorithms.h>
2024-11-25 17:50:37 +00:00
# include <LibWeb/ContentSecurityPolicy/Directives/DirectiveOperations.h>
2024-12-02 16:01:19 +00:00
# include <LibWeb/ContentSecurityPolicy/Directives/KeywordSources.h>
2024-11-25 17:29:27 +00:00
# include <LibWeb/ContentSecurityPolicy/Directives/Names.h>
# include <LibWeb/ContentSecurityPolicy/PolicyList.h>
# include <LibWeb/ContentSecurityPolicy/Violation.h>
2024-12-02 16:01:19 +00:00
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/Element.h>
2024-11-25 17:29:27 +00:00
# include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
2024-11-25 17:50:37 +00:00
# include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
2025-08-07 22:41:54 +02:00
# include <LibWeb/Fetch/Infrastructure/URL.h>
2024-12-04 14:09:28 +00:00
# include <LibWeb/HTML/Window.h>
2025-08-07 22:41:54 +02:00
# include <LibWeb/HTML/WorkerGlobalScope.h>
2024-12-02 16:01:19 +00:00
# include <LibWeb/Infra/Strings.h>
2025-08-07 22:41:54 +02:00
# include <LibWeb/SRI/SRI.h>
2024-12-02 16:01:19 +00:00
# include <LibWeb/WebAssembly/WebAssembly.h>
2024-11-25 17:29:27 +00:00
namespace Web : : ContentSecurityPolicy {
// https://w3c.github.io/webappsec-csp/#does-resource-hint-violate-policy
2025-06-20 18:03:13 +01:00
[[nodiscard]] static GC : : Ptr < Directives : : Directive > does_resource_hint_request_violate_policy ( GC : : Heap & heap , GC : : Ref < Fetch : : Infrastructure : : Request const > request , GC : : Ref < Policy const > policy )
2024-11-25 17:29:27 +00:00
{
// 1. Let defaultDirective be policy’ s first directive whose name is "default-src".
auto default_directive_iterator = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : DefaultSrc ;
} ) ;
// 2. If defaultDirective does not exist, return "Does Not Violate".
if ( default_directive_iterator . is_end ( ) )
return { } ;
// 3. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. Let result be the result of executing directive’ s pre-request check on request and policy.
2025-06-20 18:03:13 +01:00
auto result = directive - > pre_request_check ( heap , request , policy ) ;
2024-11-25 17:29:27 +00:00
// 2. If result is "Allowed", then return "Does Not Violate".
if ( result = = Directives : : Directive : : Result : : Allowed ) {
return { } ;
}
}
// 4. Return defaultDirective.
return * default_directive_iterator ;
}
// https://w3c.github.io/webappsec-csp/#does-request-violate-policy
2025-06-20 18:03:13 +01:00
[[nodiscard]] static GC : : Ptr < Directives : : Directive > does_request_violate_policy ( GC : : Heap & heap , GC : : Ref < Fetch : : Infrastructure : : Request const > request , GC : : Ref < Policy const > policy )
2024-11-25 17:29:27 +00:00
{
// 1. If request’ s initiator is "prefetch", then return the result of executing § 6.7.2.2 Does resource hint
// request violate policy? on request and policy.
if ( request - > initiator ( ) = = Fetch : : Infrastructure : : Request : : Initiator : : Prefetch )
2025-06-20 18:03:13 +01:00
return does_resource_hint_request_violate_policy ( heap , request , policy ) ;
2024-11-25 17:29:27 +00:00
// 2. Let violates be "Does Not Violate".
GC : : Ptr < Directives : : Directive > violates ;
// 3. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. Let result be the result of executing directive’ s pre-request check on request and policy.
2025-06-20 18:03:13 +01:00
auto result = directive - > pre_request_check ( heap , request , policy ) ;
2024-11-25 17:29:27 +00:00
// 2. If result is "Blocked", then let violates be directive.
if ( result = = Directives : : Directive : : Result : : Blocked ) {
violates = directive ;
}
}
// 4. Return violates.
return violates ;
}
2024-11-28 11:57:04 +00:00
// https://w3c.github.io/webappsec-csp/#report-for-request
void report_content_security_policy_violations_for_request ( JS : : Realm & realm , GC : : Ref < Fetch : : Infrastructure : : Request > request )
{
// 1. Let CSP list be request’ s policy container's CSP list.
auto csp_list = request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) - > csp_list ;
// 2. For each policy of CSP list:
for ( auto policy : csp_list - > policies ( ) ) {
// 1. If policy’ s disposition is "enforce", then skip to the next policy.
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
continue ;
// 2. Let violates be the result of executing § 6.7.2.1 Does request violate policy? on request and policy.
2025-06-20 18:03:13 +01:00
auto violates = does_request_violate_policy ( realm . heap ( ) , request , policy ) ;
2024-11-28 11:57:04 +00:00
// 3. If violates is not "Does Not Violate", then execute § 5.5 Report a violation on the result of executing
// § 2.4.2 Create a violation object for request, and policy. on request, and policy.
if ( violates ) {
auto violation = Violation : : create_a_violation_object_for_request_and_policy ( realm , request , policy ) ;
violation - > report_a_violation ( realm ) ;
}
}
}
2024-11-28 12:30:36 +00:00
// https://w3c.github.io/webappsec-csp/#should-block-request
2024-11-25 17:29:27 +00:00
Directives : : Directive : : Result should_request_be_blocked_by_content_security_policy ( JS : : Realm & realm , GC : : Ref < Fetch : : Infrastructure : : Request > request )
{
// 1. Let CSP list be request’ s policy container's CSP list.
auto csp_list = request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) - > csp_list ;
// 2. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 3. For each policy of CSP list:
for ( auto policy : csp_list - > policies ( ) ) {
// 1. If policy’ s disposition is "report", then skip to the next policy.
if ( policy - > disposition ( ) = = Policy : : Disposition : : Report )
continue ;
// 2. Let violates be the result of executing § 6.7.2.1 Does request violate policy? on request and policy.
2025-06-20 18:03:13 +01:00
auto violates = does_request_violate_policy ( realm . heap ( ) , request , policy ) ;
2024-11-25 17:29:27 +00:00
// 3. If violates is not "Does Not Violate", then:
if ( violates ) {
// 1. Execute § 5.5 Report a violation on the result of executing § 2.4.2 Create a violation object for
// request, and policy. on request, and policy.
auto violation = Violation : : create_a_violation_object_for_request_and_policy ( realm , request , policy ) ;
violation - > report_a_violation ( realm ) ;
// 2. Set result to "Blocked".
result = Directives : : Directive : : Result : : Blocked ;
}
}
// 4. Return result.
return result ;
}
2025-08-07 22:41:54 +02:00
// https://w3c.github.io/webappsec-subresource-integrity/#should-request-be-blocked-by-integrity-policy
Directives : : Directive : : Result should_request_be_blocked_by_integrity_policy ( GC : : Ref < Fetch : : Infrastructure : : Request > request )
{
VERIFY ( request - > policy_container ( ) . has < GC : : Ref < HTML : : PolicyContainer > > ( ) ) ;
// 1. Let policyContainer be request’ s policy container.
auto const & policy_container = request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) ;
// 2. Let parsedMetadata be the result of calling parse metadata with request’ s integrity metadata.
auto parsed_metadata = MUST ( SRI : : parse_metadata ( request - > integrity_metadata ( ) ) ) ;
// 3. If parsedMetadata is not the empty set and request’ s mode is either "cors" or "same-origin", return "Allowed".
if ( ! parsed_metadata . is_empty ( ) & & ( request - > mode ( ) = = Fetch : : Infrastructure : : Request : : Mode : : CORS | | request - > mode ( ) = = Fetch : : Infrastructure : : Request : : Mode : : SameOrigin ) )
return Directives : : Directive : : Result : : Allowed ;
// 4. If request’ s url is local, return "Allowed".
if ( Fetch : : Infrastructure : : is_local_url ( request - > url ( ) ) )
return Directives : : Directive : : Result : : Allowed ;
// 5. Let policy be policyContainer’ s integrity policy.
auto const & policy = policy_container - > integrity_policy ;
// 6. Let reportPolicy be policyContainer’ s report only integrity policy.
auto const & report_policy = policy_container - > report_only_integrity_policy ;
// 7. If both policy and reportPolicy are empty integrity policys, return "Allowed".
if ( policy . is_empty ( ) & & report_policy . is_empty ( ) )
return Directives : : Directive : : Result : : Allowed ;
// 8. Let global be request’ s client’ s global object.
auto & global = request - > client ( ) - > global_object ( ) ;
// 9. If global is not a Window nor a WorkerGlobalScope, return "Allowed".
if ( ! is < HTML : : Window > ( global ) & & ! is < HTML : : WorkerGlobalScope > ( global ) )
return Directives : : Directive : : Result : : Allowed ;
// 10. Let block be a boolean, initially false.
bool block = false ;
// FIXME: 11. Let reportBlock be a boolean, initially false.
[[maybe_unused]] auto report_block = false ;
// 12. If policy’ s sources contains "inline" and policy’ s blocked destinations contains request’ s destination, set block to true.
if ( policy . sources . contains_slow ( " inline " sv )
& & request - > destination ( ) . has_value ( )
& & policy . blocked_destinations . contains_slow ( request - > destination ( ) . value ( ) ) )
block = true ;
// 13. If reportPolicy’ s sources contains "inline" and reportPolicy’ s blocked destinations contains request’ s destination, set reportBlock to true.
if ( report_policy . sources . contains_slow ( " inline " sv )
& & request - > destination ( ) . has_value ( )
& & report_policy . blocked_destinations . contains_slow ( request - > destination ( ) . value ( ) ) )
report_block = true ;
// FIXME: 14. If block is true or reportBlock is true, then report violation with request, block, reportBlock, policy and reportPolicy.
// 15. If block is true, then return "Blocked"; otherwise "Allowed".
return block ? Directives : : Directive : : Result : : Blocked : Directives : : Directive : : Result : : Allowed ;
}
2024-11-28 12:30:36 +00:00
// https://w3c.github.io/webappsec-csp/#should-block-response
Directives : : Directive : : Result should_response_to_request_be_blocked_by_content_security_policy ( JS : : Realm & realm , GC : : Ref < Fetch : : Infrastructure : : Response > response , GC : : Ref < Fetch : : Infrastructure : : Request > request )
{
// 1. Let CSP list be request’ s policy container's CSP list.
auto csp_list = request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) - > csp_list ;
// 2. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 3. For each policy of CSP list:
// Spec Note: This portion of the check verifies that the page can load the response. That is, that a Service
// Worker hasn't substituted a file which would violate the page’ s CSP.
for ( auto policy : csp_list - > policies ( ) ) {
// 1. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. If the result of executing directive’ s post-request check is "Blocked", then:
2025-06-20 18:03:13 +01:00
if ( directive - > post_request_check ( realm . heap ( ) , request , response , policy ) = = Directives : : Directive : : Result : : Blocked ) {
2024-11-28 12:30:36 +00:00
// 1. Execute § 5.5 Report a violation on the result of executing § 2.4.2 Create a violation object for
// request, and policy. on request, and policy.
auto violation = Violation : : create_a_violation_object_for_request_and_policy ( realm , request , policy ) ;
violation - > report_a_violation ( realm ) ;
// 2. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce ) {
result = Directives : : Directive : : Result : : Blocked ;
}
}
}
}
// 4. Return result.
return result ;
}
2024-11-25 17:50:37 +00:00
// https://w3c.github.io/webappsec-csp/#should-block-navigation-request
Directives : : Directive : : Result should_navigation_request_of_type_be_blocked_by_content_security_policy ( GC : : Ref < Fetch : : Infrastructure : : Request > navigation_request , Directives : : Directive : : NavigationType navigation_type )
{
// 1. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 2. For each policy of navigation request’ s policy container’ s CSP list:
auto policy_container = navigation_request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) ;
for ( auto policy : policy_container - > csp_list - > policies ( ) ) {
// 1. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. If directive’ s pre-navigation check returns "Allowed" when executed upon navigation request, type, and policy skip to the next directive.
auto directive_result = directive - > pre_navigation_check ( navigation_request , navigation_type , policy ) ;
if ( directive_result = = Directives : : Directive : : Result : : Allowed )
continue ;
// 2. Otherwise, let violation be the result of executing § 2.4.1 Create a violation object for global, policy, and directive on navigation request’ s
// client’ s global object, policy, and directive’ s name.
auto & realm = navigation_request - > client ( ) - > realm ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , navigation_request - > client ( ) - > global_object ( ) , policy , directive - > name ( ) ) ;
// 3. Set violation’ s resource to navigation request’ s URL.
violation - > set_resource ( navigation_request - > url ( ) ) ;
// 4. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 5. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
// 3. If result is "Allowed", and if navigation request’ s current URL’ s scheme is javascript:
if ( result = = Directives : : Directive : : Result : : Allowed & & navigation_request - > current_url ( ) . scheme ( ) = = " javascript " sv ) {
// 1. For each policy of navigation request’ s policy container’ s CSP list:
VERIFY ( navigation_request - > policy_container ( ) . has < GC : : Ref < HTML : : PolicyContainer > > ( ) ) ;
auto csp_list = navigation_request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) - > csp_list ;
for ( auto policy : csp_list - > policies ( ) ) {
// 1. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. Let directive-name be the result of executing § 6.8.2 Get the effective directive for inline
// checks on type.
// FIXME: File spec issue that the type should probably always be "navigation", as NavigationType would
// cause this algorithm to return null, making directive-name null, then piping directive-name
// into a Violation object where the directive name is defined to be a non-empty string.
// Other parts of the spec seem to refer to the "navigation" inline type as being for
// javascript: URLs. Additionally, this doesn't have an impact on the security decision here,
// just which directive is reported to have been violated.
auto directive_name = Directives : : get_the_effective_directive_for_inline_checks ( Directives : : Directive : : InlineType : : Navigation ) ;
// 2. If directive’ s inline check returns "Allowed" when executed upon null, "navigation" and
// navigation request’ s current URL, skip to the next directive.
// FIXME: File spec issue that they forgot to pass in "policy" here.
// FIXME: File spec issue that current URL is a URL object and not a string, therefore they must use a
// spec operation to serialize the URL.
auto & realm = navigation_request - > client ( ) - > realm ( ) ;
auto serialized_url = navigation_request - > current_url ( ) . to_string ( ) ;
2025-06-20 18:03:13 +01:00
if ( directive - > inline_check ( realm . heap ( ) , nullptr , Directives : : Directive : : InlineType : : Navigation , policy , serialized_url ) = = Directives : : Directive : : Result : : Allowed )
2024-11-25 17:50:37 +00:00
continue ;
// 3. Otherwise, let violation be the result of executing § 2.4.1 Create a violation object for global,
// policy, and directive on navigation request’ s client’ s global object, policy, and directive-name.
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , navigation_request - > client ( ) - > global_object ( ) , policy , directive_name . to_string ( ) ) ;
// 4. Set violation’ s resource to navigation request’ s URL.
violation - > set_resource ( navigation_request - > url ( ) ) ;
// 5. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 6. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
}
// 4. Return result.
return result ;
}
// https://w3c.github.io/webappsec-csp/#should-block-navigation-response
Directives : : Directive : : Result should_navigation_response_to_navigation_request_of_type_in_target_be_blocked_by_content_security_policy (
GC : : Ptr < Fetch : : Infrastructure : : Request > navigation_request ,
GC : : Ref < Fetch : : Infrastructure : : Response > navigation_response ,
GC : : Ref < PolicyList > response_csp_list ,
Directives : : Directive : : NavigationType navigation_type ,
GC : : Ref < HTML : : Navigable > target )
{
// 1. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// FIXME: File spec issue stating that the request can be null (e.g. from a srcdoc resource).
if ( ! navigation_request ) {
dbgln ( " FIXME: Handle null navigation_request in navigation response Content Security Policy check. " ) ;
return result ;
}
// 2. For each policy of response CSP list:
for ( auto policy : response_csp_list - > policies ( ) ) {
// Spec Note: Some directives (like frame-ancestors) allow a response’ s Content Security Policy to act on the navigation.
// 1. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. If directive’ s navigation response check returns "Allowed" when executed upon navigation request, type, navigation response, target, "response", and policy skip to the next directive.
auto directive_result = directive - > navigation_response_check ( * navigation_request , navigation_type , navigation_response , target , Directives : : Directive : : CheckType : : Response , policy ) ;
if ( directive_result = = Directives : : Directive : : Result : : Allowed )
continue ;
// 2. Otherwise, let violation be the result of executing § 2.4.1 Create a violation object for global, policy, and directive on null, policy, and directive’ s name.
// Spec Note: We use null for the global object, as no global exists: we haven’ t processed the navigation to create a Document yet.
// FIXME: What should the realm be here?
auto & realm = navigation_request - > client ( ) - > realm ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , nullptr , policy , directive - > name ( ) ) ;
// 3. Set violation’ s resource to navigation response’ s URL.
if ( navigation_response - > url ( ) . has_value ( ) ) {
violation - > set_resource ( navigation_response - > url ( ) . value ( ) ) ;
} else {
violation - > set_resource ( Empty { } ) ;
}
// 4. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 5. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
// 3. For each policy of navigation request’ s policy container’ s CSP list:
auto request_policy_container = navigation_request - > policy_container ( ) . get < GC : : Ref < HTML : : PolicyContainer > > ( ) ;
for ( auto policy : request_policy_container - > csp_list - > policies ( ) ) {
// Spec Note: NOTE: Some directives in the navigation request’ s context (like frame-ancestors) need the response before acting on the navigation.
// 1. For each directive of policy:
for ( auto directive : policy - > directives ( ) ) {
// 1. If directive’ s navigation response check returns "Allowed" when executed upon navigation request, type, navigation response, target, "source", and policy skip to the next directive.
auto directive_result = directive - > navigation_response_check ( * navigation_request , navigation_type , navigation_response , target , Directives : : Directive : : CheckType : : Source , policy ) ;
if ( directive_result = = Directives : : Directive : : Result : : Allowed )
continue ;
// 2. Otherwise, let violation be the result of executing § 2.4.1 Create a violation object for global, policy, and directive on navigation request’ s client’ s global object, policy, and directive’ s name.
auto & realm = navigation_request - > client ( ) - > realm ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , navigation_request - > client ( ) - > global_object ( ) , policy , directive - > name ( ) ) ;
// 3. Set violation’ s resource to navigation request’ s URL.
violation - > set_resource ( navigation_request - > url ( ) ) ;
// 4. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 5. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
// 4. Return result.
return result ;
}
2024-12-02 16:01:19 +00:00
// https://w3c.github.io/webappsec-csp/#should-block-inline
Directives : : Directive : : Result should_elements_inline_type_behavior_be_blocked_by_content_security_policy ( JS : : Realm & realm , GC : : Ref < DOM : : Element > element , Directives : : Directive : : InlineType type , String const & source )
{
// Spec Note: The valid values for type are "script", "script attribute", "style", and "style attribute".
VERIFY ( type = = Directives : : Directive : : InlineType : : Script | | type = = Directives : : Directive : : InlineType : : ScriptAttribute | | type = = Directives : : Directive : : InlineType : : Style | | type = = Directives : : Directive : : InlineType : : StyleAttribute ) ;
// 1. Assert: element is not null.
// NOTE: Already done by only accepting a GC::Ref.
// 2. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 3. For each policy of element’ s Document's global object’ s CSP list:
auto & global_object = element - > document ( ) . realm ( ) . global_object ( ) ;
auto csp_list = PolicyList : : from_object ( global_object ) ;
VERIFY ( csp_list ) ;
for ( auto const policy : csp_list - > policies ( ) ) {
// 1. For each directive of policy’ s directive set:
for ( auto const directive : policy - > directives ( ) ) {
// 1. If directive’ s inline check returns "Allowed" when executed upon element, type, policy and source,
// skip to the next directive.
if ( directive - > inline_check ( realm . heap ( ) , element , type , policy , source ) = = Directives : : Directive : : Result : : Allowed )
continue ;
// 2. Let directive-name be the result of executing § 6.8.2 Get the effective directive for inline checks
// on type.
auto directive_name = Directives : : get_the_effective_directive_for_inline_checks ( type ) ;
// 3. Otherwise, let violation be the result of executing § 2.4.1 Create a violation object for global,
// policy, and directive on the current settings object’ s global object, policy, and directive-name.
// FIXME: File spec issue about using "current settings object" here, as it can run outside of a script
// context (for example, a just parsed inline script being prepared)
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , global_object , policy , directive_name . to_string ( ) ) ;
// 4. Set violation’ s resource to "inline".
violation - > set_resource ( Violation : : Resource : : Inline ) ;
// 5. Set violation’ s element to element.
violation - > set_element ( element ) ;
// 6. If directive’ s value contains the expression "'report-sample'", then set violation’ s sample to the
// substring of source containing its first 40 characters.
// FIXME: Should this be case insensitive?
auto maybe_report_sample = directive - > value ( ) . find_if ( [ ] ( auto const & directive_value ) {
return directive_value . equals_ignoring_ascii_case ( Directives : : KeywordSources : : ReportSample ) ;
} ) ;
if ( ! maybe_report_sample . is_end ( ) ) {
Utf8View source_view { source } ;
auto sample = source_view . unicode_substring_view ( 0 , min ( source_view . length ( ) , 40 ) ) ;
violation - > set_sample ( String : : from_utf8_without_validation ( sample . as_string ( ) . bytes ( ) ) ) ;
}
// 7. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 8. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce ) {
result = Directives : : Directive : : Result : : Blocked ;
}
}
}
// 4. Return result.
return result ;
}
// https://w3c.github.io/webappsec-csp/#can-compile-strings
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 )
{
// FIXME: 1. If compilationType is "TIMER", then:
// 1. Let sourceString be codeString.
StringView source_string = code_string ;
// FIXME: 2. Else:
// FIXME: We don't do these two steps as we don't currently support Trusted Types.
// 3. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 4. Let global be realm’ s global object.
auto & global = realm . global_object ( ) ;
// 5. For each policy of global’ s CSP list:
auto csp_list = PolicyList : : from_object ( global ) ;
VERIFY ( csp_list ) ;
for ( auto const policy : csp_list - > policies ( ) ) {
// 1. Let source-list be null.
Optional < Vector < String > > maybe_source_list ;
// 2. If policy contains a directive whose name is "script-src", then set source-list to that directive's value.
auto maybe_script_src = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : ScriptSrc ;
} ) ;
if ( ! maybe_script_src . is_end ( ) ) {
maybe_source_list = ( * maybe_script_src ) - > value ( ) ;
} else {
// Otherwise if policy contains a directive whose name is "default-src", then set source-list to that
// directive’ s value.
auto maybe_default_src = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : DefaultSrc ;
} ) ;
if ( ! maybe_default_src . is_end ( ) )
maybe_source_list = ( * maybe_default_src ) - > value ( ) ;
}
// 3. If source-list is not null, and does not contain a source expression which is an ASCII case-insensitive
// match for the string "'unsafe-eval'", then:
if ( maybe_source_list . has_value ( ) ) {
auto const & source_list = maybe_source_list . value ( ) ;
auto maybe_unsafe_eval = source_list . find_if ( [ ] ( auto const & directive_value ) {
return directive_value . equals_ignoring_ascii_case ( Directives : : KeywordSources : : UnsafeEval ) ;
} ) ;
if ( maybe_unsafe_eval . is_end ( ) ) {
// 1. Let violation be the result of executing § 2.4.1 Create a violation object for global, policy,
// and directive on global, policy, and "script-src".
auto script_src_string = Directives : : Names : : ScriptSrc . to_string ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , global , policy , script_src_string ) ;
// 2. Set violation’ s resource to "eval".
violation - > set_resource ( Violation : : Resource : : Eval ) ;
// 3. If source-list contains the expression "'report-sample'", then set violation’ s sample to the
// substring of sourceString containing its first 40 characters.
// FIXME: Should this be case insensitive?
auto maybe_report_sample = source_list . find_if ( [ ] ( auto const & directive_value ) {
return directive_value . equals_ignoring_ascii_case ( Directives : : KeywordSources : : ReportSample ) ;
} ) ;
if ( ! maybe_report_sample . is_end ( ) ) {
Utf8View source_view { source_string } ;
auto sample = source_view . unicode_substring_view ( 0 , min ( source_view . length ( ) , 40 ) ) ;
violation - > set_sample ( String : : from_utf8_without_validation ( sample . as_string ( ) . bytes ( ) ) ) ;
}
// 4. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 5. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
}
// 6. If result is "Blocked", throw an EvalError exception.
if ( result = = Directives : : Directive : : Result : : Blocked ) {
return realm . vm ( ) . throw_completion < JS : : EvalError > ( " Blocked by Content Security Policy " sv ) ;
}
return { } ;
}
// https://w3c.github.io/webappsec-csp/#can-compile-wasm-bytes
JS : : ThrowCompletionOr < void > ensure_csp_does_not_block_wasm_byte_compilation ( JS : : Realm & realm )
{
// 1. Let global be realm’ s global object.
auto & global = realm . global_object ( ) ;
// 2. Let result be "Allowed".
auto result = Directives : : Directive : : Result : : Allowed ;
// 3. For each policy of global’ s CSP list:
auto csp_list = PolicyList : : from_object ( global ) ;
VERIFY ( csp_list ) ;
for ( auto const policy : csp_list - > policies ( ) ) {
// 1. Let source-list be null.
Optional < Vector < String > > maybe_source_list ;
// 2. If policy contains a directive whose name is "script-src", then set source-list to that directive's value.
auto maybe_script_src = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : ScriptSrc ;
} ) ;
if ( ! maybe_script_src . is_end ( ) ) {
maybe_source_list = ( * maybe_script_src ) - > value ( ) ;
} else {
// Otherwise if policy contains a directive whose name is "default-src", then set source-list to that
// directive’ s value.
auto maybe_default_src = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : DefaultSrc ;
} ) ;
if ( ! maybe_default_src . is_end ( ) )
maybe_source_list = ( * maybe_default_src ) - > value ( ) ;
}
// 3. If source-list is non-null, and does not contain a source expression which is an ASCII case-insensitive
// match for the string "'unsafe-eval'", and does not contain a source expression which is an ASCII
// case-insensitive match for the string "'wasm-unsafe-eval'", then:
if ( maybe_source_list . has_value ( ) ) {
auto const & source_list = maybe_source_list . value ( ) ;
auto maybe_unsafe_eval = source_list . find_if ( [ ] ( auto const & directive_value ) {
return directive_value . equals_ignoring_ascii_case ( Directives : : KeywordSources : : UnsafeEval )
| | directive_value . equals_ignoring_ascii_case ( Directives : : KeywordSources : : WasmUnsafeEval ) ;
} ) ;
if ( maybe_unsafe_eval . is_end ( ) ) {
// 1. Let violation be the result of executing § 2.4.1 Create a violation object for global, policy,
// and directive on global, policy, and "script-src".
auto script_src_string = Directives : : Names : : ScriptSrc . to_string ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , global , policy , script_src_string ) ;
// 2. Set violation’ s resource to "wasm-eval".
violation - > set_resource ( Violation : : Resource : : WasmEval ) ;
// 3. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 4. If policy’ s disposition is "enforce", then set result to "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
result = Directives : : Directive : : Result : : Blocked ;
}
}
}
// 4. If result is "Blocked", throw a WebAssembly.CompileError exception.
if ( result = = Directives : : Directive : : Result : : Blocked ) {
return realm . vm ( ) . throw_completion < WebAssembly : : CompileError > ( " Blocked by Content Security Policy " sv ) ;
}
return { } ;
}
2024-12-04 14:09:28 +00:00
// https://w3c.github.io/webappsec-csp/#allow-base-for-document
Directives : : Directive : : Result is_base_allowed_for_document ( JS : : Realm & realm , URL : : URL const & base , GC : : Ref < DOM : : Document const > document )
{
// 1. For each policy of document’ s global object’ s csp list:
2025-08-16 22:01:27 +12:00
auto csp_list = PolicyList : : from_object ( document - > realm ( ) . global_object ( ) ) ;
2024-12-04 14:09:28 +00:00
VERIFY ( csp_list ) ;
for ( auto const policy : csp_list - > policies ( ) ) {
// 1. Let source list be null.
// NOTE: Not necessary.
// 2. If a directive whose name is "base-uri" is present in policy’ s directive set, set source list to that
// directive’ s value.
auto maybe_base_uri = policy - > directives ( ) . find_if ( [ ] ( auto const & directive ) {
return directive - > name ( ) = = Directives : : Names : : BaseUri ;
} ) ;
// 3. If source list is null, skip to the next policy.
if ( maybe_base_uri . is_end ( ) )
continue ;
auto const & source_list = ( * maybe_base_uri ) - > value ( ) ;
// 4. If the result of executing § 6.7.2.7 Does url match source list in origin with redirect count? on base,
// source list, policy’ s self-origin, and 0 is "Does Not Match":
// Spec Note: We compare against the fallback base URL in order to deal correctly with things like an iframe
// srcdoc Document which has been sandboxed into an opaque origin.
if ( Directives : : does_url_match_source_list_in_origin_with_redirect_count ( base , source_list , policy - > self_origin ( ) , 0 ) = = Directives : : MatchResult : : DoesNotMatch ) {
// 1. Let violation be the result of executing § 2.4.1 Create a violation object for global, policy, and
// directive on document’ s global object, policy, and "base-uri".
auto base_uri_string = Directives : : Names : : BaseUri . to_string ( ) ;
auto violation = Violation : : create_a_violation_object_for_global_policy_and_directive ( realm , document - > window ( ) , policy , base_uri_string ) ;
// 2. Set violation’ s resource to "inline".
violation - > set_resource ( Violation : : Resource : : Inline ) ;
// 3. Execute § 5.5 Report a violation on violation.
violation - > report_a_violation ( realm ) ;
// 4. If policy’ s disposition is "enforce", return "Blocked".
if ( policy - > disposition ( ) = = Policy : : Disposition : : Enforce )
return Directives : : Directive : : Result : : Blocked ;
}
}
// 2. Return "Allowed".
return Directives : : Directive : : Result : : Allowed ;
}
2024-11-25 17:29:27 +00:00
}