diff --git a/Libraries/LibWeb/DOMURL/DOMURL.cpp b/Libraries/LibWeb/DOMURL/DOMURL.cpp index cb7f5e17ce60..2981aee7f324 100644 --- a/Libraries/LibWeb/DOMURL/DOMURL.cpp +++ b/Libraries/LibWeb/DOMURL/DOMURL.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2021, Idan Horowitz * Copyright (c) 2021, the SerenityOS developers. * Copyright (c) 2023, networkException - * Copyright (c) 2024, Shannon Booth + * Copyright (c) 2024-2025, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -136,17 +136,21 @@ void DOMURL::revoke_object_url(JS::VM&, StringView url) if (url_record.scheme() != "blob"sv) return; - // 3. Let origin be the origin of url record. - auto origin = url_record.origin(); + // 3. Let entry be urlRecord’s blob URL entry. + auto& entry = url_record.blob_url_entry(); - // 4. Let settings be the current settings object. - auto& settings = HTML::current_principal_settings_object(); + // 4. If entry is null, return. + if (!entry.has_value()) + return; + + // 5. Let isAuthorized be the result of checking for same-partition blob URL usage with entry and the current settings object. + bool is_authorized = FileAPI::check_for_same_partition_blob_url_usage(entry.value(), HTML::current_principal_settings_object()); - // 5. If origin is not same origin with settings’s origin, return. - if (!origin.is_same_origin(settings.origin())) + // 6. If isAuthorized is false, then return. + if (!is_authorized) return; - // 6. Remove an entry from the Blob URL Store for url. + // 7. Remove an entry from the Blob URL Store for url. FileAPI::remove_entry_from_blob_url_store(url); } diff --git a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 934626b731b9..d0347e612eb3 100644 --- a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -3,6 +3,7 @@ * Copyright (c) 2023, Luke Wilde * Copyright (c) 2023, Sam Atkins * Copyright (c) 2024, Jamie Mansfield + * Copyright (c) 2025, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -595,6 +596,21 @@ WebIDL::ExceptionOr> main_fetch(JS::Realm& realm, Infra return GC::Ptr {}; } +// https://fetch.spec.whatwg.org/#request-determine-the-environment +static GC::Ptr determine_the_environment(GC::Ref request) +{ + // 1. If request’s reserved client is non-null, then return request’s reserved client. + if (request->reserved_client()) + return request->reserved_client(); + + // 2. If request’s client is non-null, then return request’s client. + if (request->client()) + return request->client(); + + // 3. Return null. + return {}; +} + // https://fetch.spec.whatwg.org/#fetch-finale void fetch_response_handover(JS::Realm& realm, Infrastructure::FetchParams const& fetch_params, Infrastructure::Response& response) { @@ -817,30 +833,52 @@ WebIDL::ExceptionOr> scheme_fetch(JS::Realm& realm, Inf // 1. Let blobURLEntry be request’s current URL’s blob URL entry. auto const& blob_url_entry = request->current_url().blob_url_entry(); - // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s object is not a Blob object, - // then return a network error. [FILEAPI] + // 2. If request’s method is not `GET` or blobURLEntry is null, then return a network error. [FILEAPI] if (request->method() != "GET"sv.bytes() || !blob_url_entry.has_value()) { // FIXME: Handle "blobURLEntry’s object is not a Blob object". It could be a MediaSource object, but we // have not yet implemented the Media Source Extensions spec. return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has an invalid 'blob:' URL"sv)); } - // 3. Let blob be blobURLEntry’s object. - auto const blob = FileAPI::Blob::create(realm, blob_url_entry.value().object.data, blob_url_entry.value().object.type); + // 3. Let requestEnvironment be the result of determining the environment given request. + auto request_environment = determine_the_environment(request); + + // 4. Let isTopLevelNavigation be true if request’s destination is "document"; otherwise, false. + bool is_top_level_navigation = request->destination() == Infrastructure::Request::Destination::Document; + + // 5. If isTopLevelNavigation is false and requestEnvironment is null, then return a network error. + if (!is_top_level_navigation && !request_environment) + return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request is missing fetch client"sv)); + + // 6. Let navigationOrEnvironment be the string "navigation" if isTopLevelNavigation is true; otherwise, requestEnvironment. + auto navigation_or_environment = [&]() -> Variant> { + if (is_top_level_navigation) + return FileAPI::NavigationEnvironment {}; + return GC::Ref { *request_environment }; + }(); + + // 7. Let blob be the result of obtaining a blob object given blobURLEntry and navigationOrEnvironment. + auto blob_object = FileAPI::obtain_a_blob_object(blob_url_entry.value(), navigation_or_environment); + + // 8. If blob is not a Blob object, then return a network error. + // FIXME: This should probably check for a MediaSource object as well, once we implement that. + if (!blob_object.has_value()) + return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Failed to obtain a Blob object from 'blob:' URL"sv)); + auto const blob = FileAPI::Blob::create(realm, blob_object->data, blob_object->type); - // 4. Let response be a new response. + // 9. Let response be a new response. auto response = Infrastructure::Response::create(vm); - // 5. Let fullLength be blob’s size. + // 10. Let fullLength be blob’s size. auto full_length = blob->size(); - // 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded. + // 11. Let serializedFullLength be fullLength, serialized and isomorphic encoded. auto serialized_full_length = String::number(full_length); - // 7. Let type be blob’s type. + // 12. Let type be blob’s type. auto const& type = blob->type(); - // 8. If request’s header list does not contain `Range`: + // 13. If request’s header list does not contain `Range`: if (!request->header_list()->contains("Range"sv.bytes())) { // 1. Let bodyWithType be the result of safely extracting blob. auto body_with_type = safely_extract_body(realm, blob->raw_bytes()); @@ -858,7 +896,7 @@ WebIDL::ExceptionOr> scheme_fetch(JS::Realm& realm, Inf auto content_type_header = Infrastructure::Header::from_string_pair("Content-Type"sv, type); response->header_list()->append(move(content_type_header)); } - // 9. Otherwise: + // 14. Otherwise: else { // 1. Set response’s range-requested flag. response->set_range_requested(true); @@ -933,7 +971,7 @@ WebIDL::ExceptionOr> scheme_fetch(JS::Realm& realm, Inf response->header_list()->append(move(content_range_header)); } - // 10. Return response. + // 15. Return response. return PendingResponse::create(vm, request, response); } // -> "data" diff --git a/Libraries/LibWeb/FileAPI/BlobURLStore.cpp b/Libraries/LibWeb/FileAPI/BlobURLStore.cpp index f73cc5ab50b3..d76cf21fc9a4 100644 --- a/Libraries/LibWeb/FileAPI/BlobURLStore.cpp +++ b/Libraries/LibWeb/FileAPI/BlobURLStore.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2023, Tim Flynn * Copyright (c) 2024, Andreas Kling - * Copyright (c) 2024, Shannon Booth + * Copyright (c) 2024-2025, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -14,6 +14,7 @@ #include #include #include +#include namespace Web::FileAPI { @@ -78,6 +79,41 @@ ErrorOr add_entry_to_blob_url_store(GC::Ref object) return url; } +// https://www.w3.org/TR/FileAPI/#check-for-same-partition-blob-url-usage +bool check_for_same_partition_blob_url_usage(URL::BlobURLEntry const& blob_url_entry, GC::Ref environment) +{ + // 1. Let blobStorageKey be the result of obtaining a storage key for non-storage purposes with blobUrlEntry’s environment. + auto blob_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(blob_url_entry.environment.origin); + + // 2. Let environmentStorageKey be the result of obtaining a storage key for non-storage purposes with environment. + auto environment_storage_key = StorageAPI::obtain_a_storage_key_for_non_storage_purposes(environment); + + // 3. If blobStorageKey is not equal to environmentStorageKey, then return false. + if (blob_storage_key != environment_storage_key) + return false; + + // 4. Return true. + return true; +} + +// https://www.w3.org/TR/FileAPI/#blob-url-obtain-object +Optional obtain_a_blob_object(URL::BlobURLEntry const& blob_url_entry, Variant, NavigationEnvironment> environment) +{ + // 1. Let isAuthorized be true. + bool is_authorized = true; + + // 2. If environment is not the string "navigation", then set isAuthorized to the result of checking for same-partition blob URL usage with blobUrlEntry and environment. + if (!environment.has()) + is_authorized = check_for_same_partition_blob_url_usage(blob_url_entry, environment.get>()); + + // 3. If isAuthorized is false, then return failure. + if (!is_authorized) + return {}; + + // 4. Return blobUrlEntry’s object. + return blob_url_entry.object; +} + // https://w3c.github.io/FileAPI/#removeTheEntry void remove_entry_from_blob_url_store(StringView url) { diff --git a/Libraries/LibWeb/FileAPI/BlobURLStore.h b/Libraries/LibWeb/FileAPI/BlobURLStore.h index f082d134f057..cbcf09feffac 100644 --- a/Libraries/LibWeb/FileAPI/BlobURLStore.h +++ b/Libraries/LibWeb/FileAPI/BlobURLStore.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include namespace Web::FileAPI { @@ -27,6 +27,9 @@ using BlobURLStore = HashMap; BlobURLStore& blob_url_store(); ErrorOr generate_new_blob_url(); ErrorOr add_entry_to_blob_url_store(GC::Ref object); +bool check_for_same_partition_blob_url_usage(URL::BlobURLEntry const&, GC::Ref); +struct NavigationEnvironment { }; +Optional obtain_a_blob_object(URL::BlobURLEntry const&, Variant, NavigationEnvironment> environment); void remove_entry_from_blob_url_store(StringView url); Optional resolve_a_blob_url(URL::URL const&); diff --git a/Tests/LibWeb/Text/expected/FileAPI/Blob-partitioning.txt b/Tests/LibWeb/Text/expected/FileAPI/Blob-partitioning.txt new file mode 100644 index 000000000000..1ffd8d1cfaa3 --- /dev/null +++ b/Tests/LibWeb/Text/expected/FileAPI/Blob-partitioning.txt @@ -0,0 +1 @@ +TypeError: Failed to obtain a Blob object from 'blob:' URL diff --git a/Tests/LibWeb/Text/input/FileAPI/Blob-partitioning.html b/Tests/LibWeb/Text/input/FileAPI/Blob-partitioning.html new file mode 100644 index 000000000000..a00df69f46f0 --- /dev/null +++ b/Tests/LibWeb/Text/input/FileAPI/Blob-partitioning.html @@ -0,0 +1,42 @@ + + +