Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Libraries/LibWeb/ContentSecurityPolicy/BlockingAlgorithms.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
* Copyright (c) 2025, Kenneth Myhra <kennethmyhra@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Expand All @@ -14,8 +15,11 @@
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
#include <LibWeb/Fetch/Infrastructure/URL.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/SRI/SRI.h>
#include <LibWeb/WebAssembly/WebAssembly.h>

namespace Web::ContentSecurityPolicy {
Expand Down Expand Up @@ -131,6 +135,66 @@ Directives::Directive::Result should_request_be_blocked_by_content_security_poli
return result;
}

// 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)
Comment thread
kennethmyhra marked this conversation as resolved.
&& 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;
}

// 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Web::ContentSecurityPolicy {

void report_content_security_policy_violations_for_request(JS::Realm&, GC::Ref<Fetch::Infrastructure::Request>);
Directives::Directive::Result should_request_be_blocked_by_content_security_policy(JS::Realm&, GC::Ref<Fetch::Infrastructure::Request>);
Directives::Directive::Result should_request_be_blocked_by_integrity_policy(GC::Ref<Fetch::Infrastructure::Request>);
Directives::Directive::Result should_response_to_request_be_blocked_by_content_security_policy(JS::Realm&, GC::Ref<Fetch::Infrastructure::Response>, GC::Ref<Fetch::Infrastructure::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);
Expand Down
8 changes: 5 additions & 3 deletions Libraries/LibWeb/Fetch/Fetching/Fetching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,13 @@ WebIDL::ExceptionOr<GC::Ptr<PendingResponse>> main_fetch(JS::Realm& realm, Infra
// 6. Upgrade a mixed content request to a potentially trustworthy URL, if appropriate.
MixedContent::upgrade_a_mixed_content_request_to_a_potentially_trustworthy_url_if_appropriate(request);

// 7. If should request be blocked due to a bad port, should fetching request be blocked as mixed content, or
// should request be blocked by Content Security Policy returns blocked, then set response to a network error.
// 7. If should request be blocked due to a bad port, should fetching request be blocked as mixed content, should
// request be blocked by Content Security Policy, or should request be blocked by Integrity Policy Policy
// returns blocked, then set response to a network error.
if (Infrastructure::block_bad_port(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|| MixedContent::should_fetching_request_be_blocked_as_mixed_content(request) == Infrastructure::RequestOrResponseBlocking::Blocked
|| ContentSecurityPolicy::should_request_be_blocked_by_content_security_policy(realm, request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
|| ContentSecurityPolicy::should_request_be_blocked_by_content_security_policy(realm, request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked
|| ContentSecurityPolicy::should_request_be_blocked_by_integrity_policy(request) == ContentSecurityPolicy::Directives::Directive::Result::Blocked) {
response = Infrastructure::Response::network_error(vm, "Request was blocked"_string);
}

Expand Down
1 change: 0 additions & 1 deletion Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <LibWeb/Fetch/Infrastructure/HTTP.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/HTML/PolicyContainers.h>

namespace Web::Fetch::Infrastructure {

Expand Down
18 changes: 18 additions & 0 deletions Libraries/LibWeb/HTML/PolicyContainers.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@
#include <LibGC/CellAllocator.h>
#include <LibJS/Heap/Cell.h>
#include <LibURL/Forward.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/EmbedderPolicy.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>

namespace Web::HTML {

// https://w3c.github.io/webappsec-subresource-integrity/#integrity-policy
struct IntegrityPolicy {
Vector<String> sources;
Vector<Fetch::Infrastructure::Request::Destination> blocked_destinations;
Vector<String> endpoints;

bool is_empty() const { return sources.is_empty() && blocked_destinations.is_empty() && endpoints.is_empty(); }
};

// https://html.spec.whatwg.org/multipage/origin.html#policy-container
// A policy container is a struct containing policies that apply to a Document, a WorkerGlobalScope, or a WorkletGlobalScope. It has the following items:
struct PolicyContainer : public GC::Cell {
Expand All @@ -37,6 +47,14 @@ struct PolicyContainer : public GC::Cell {
// A referrer policy, which is a referrer policy. It is initially the default referrer policy.
ReferrerPolicy::ReferrerPolicy referrer_policy { ReferrerPolicy::DEFAULT_REFERRER_POLICY };

// https://html.spec.whatwg.org/multipage/browsers.html#policy-container-integrity-policy
// An integrity policy, which is an integrity policy, initially a new integrity policy.
IntegrityPolicy integrity_policy {};

// https://html.spec.whatwg.org/multipage/browsers.html#policy-container-report-only-integrity-policy
// A report only integrity policy, which is an integrity policy, initially a new integrity policy.
IntegrityPolicy report_only_integrity_policy {};

[[nodiscard]] GC::Ref<PolicyContainer> clone(GC::Heap&) const;
[[nodiscard]] SerializedPolicyContainer serialize() const;

Expand Down
Loading