diff --git a/moli-core/tests/scripts/integrity.rs b/moli-core/tests/scripts/integrity.rs index 43fd222ba2..a7c32fbcb4 100644 --- a/moli-core/tests/scripts/integrity.rs +++ b/moli-core/tests/scripts/integrity.rs @@ -1061,20 +1061,18 @@ async fn service_worker_redirect_preserves_worker_response_filter() -> Result<() }})()"#, cross = servers.cross_origin), true, ).await?; let results: serde_json::Value = serde_json::from_str(result["value"].as_str().unwrap())?; + // A synthesized Response has an empty URL list and remains basic after + // the intercepted redirect, including its ordinary response headers. for kind in ["basic", "basic-buffered", "cors", "default"] { assert_eq!(results[kind]["body"], SCRIPT, "{kind}: {results}"); assert_eq!( results[kind]["type"], - if kind.starts_with("basic") { - "basic" - } else { - "cors" - }, + if kind != "cors" { "basic" } else { "cors" }, "{kind}: {results}" ); assert_eq!( results[kind]["privateHeader"], - if kind.starts_with("basic") { + if kind != "cors" { serde_json::json!("yes") } else { serde_json::Value::Null diff --git a/moli-fetch/src/blocking/mod.rs b/moli-fetch/src/blocking/mod.rs index 7432e6136b..eaf655501f 100644 --- a/moli-fetch/src/blocking/mod.rs +++ b/moli-fetch/src/blocking/mod.rs @@ -355,8 +355,7 @@ fn append_browser_subresource_headers( request.request_mode.as_ref().to_owned(), ); append_header_if_missing(outgoing, "Sec-Fetch-Dest", "script".to_owned()); - if let Some(origin) = request_origin_header_value(request, request_url, redirect_chain) - { + if let Some(origin) = request_origin_header_value(request, request_url) { append_header_if_missing(outgoing, "Origin", origin); } append_browser_client_hints(outgoing, config); @@ -1308,8 +1307,7 @@ mod tests { .with_request_origin(moli_url::WebOrigin::Opaque) .with_browser_request_metadata(BrowserRequestMetadata::Fetch); - let headers = - outgoing_request_headers_for_url(&config, &request, &request_url, &Vec::new(), None); + let headers = outgoing_request_headers_for_url(&config, &request, &request_url, None); assert_eq!(header_value(&headers, "origin").as_deref(), Some("null")); assert_eq!( diff --git a/moli-renderer-v8/src/network/backend/memory_cache.rs b/moli-renderer-v8/src/network/backend/memory_cache.rs index d441321726..cbe4093d25 100644 --- a/moli-renderer-v8/src/network/backend/memory_cache.rs +++ b/moli-renderer-v8/src/network/backend/memory_cache.rs @@ -62,6 +62,7 @@ pub(in crate::network) struct RawSubresourceCacheKey { page_cache_partition_id: u64, url: String, resource_type: &'static str, + follow_redirects: bool, credentials_mode: String, request_origin: Option, request_mode: String, @@ -635,6 +636,7 @@ pub(in crate::network) fn raw_subresource_memory_cache_key( page_cache_partition_id: 0, url: request.url.as_str().to_owned(), resource_type: raw_subresource_cache_resource_type_key(request.resource_type), + follow_redirects: request.follow_redirects, credentials_mode: request.credentials_mode.as_ref().to_owned(), request_origin: request .request_origin() @@ -668,7 +670,10 @@ fn raw_subresource_request_is_memory_cacheable(request: &Request) -> bool { && request.method.eq_ignore_ascii_case("GET") && request.body.is_none() && request.auth().is_none() - && request.follow_redirects + // The browser's CORS loop follows redirects itself. Its individual + // transport requests can still reuse cached responses with an empty + // URL list, in a separate bucket from automatically followed fetches. + && request.redirect_mode == moli_fetch::RequestRedirectMode::Follow && raw_subresource_memory_cacheable_headers(request) } @@ -870,6 +875,45 @@ mod tests { load } + #[test] + fn per_hop_cache_keeps_transport_redirects_and_client_origins_separate() { + let automatic = Request::get("https://cache.test/resource") + .unwrap() + .with_resource_type(RequestResourceType::Raw) + .with_browser_request_metadata(BrowserRequestMetadata::Fetch) + .with_initiator_url(&Url::parse("https://cache.test/page").unwrap()); + let per_hop = automatic.clone().with_follow_redirects(false); + let key = raw_subresource_memory_cache_key(&per_hop).expect("cacheable first CORS hop"); + let mut cache = SharedMemoryResourceCache::with_limits(usize::MAX, usize::MAX); + cache.insert_raw_subresource( + key.clone(), + raw_response(per_hop.url.as_str(), b"cached"), + u64::MAX, + ); + assert!(cache.lookup_raw_subresource(&key).is_some()); + for isolated in [ + automatic, + per_hop + .clone() + .with_request_origin(moli_url::WebOrigin::Opaque), + ] { + let isolated_key = raw_subresource_memory_cache_key(&isolated).unwrap(); + assert!( + cache.lookup_raw_subresource(&isolated_key).is_none(), + "a cached hop cannot replace an automatic redirect or another client's response" + ); + } + for mode in [ + moli_fetch::RequestRedirectMode::Manual, + moli_fetch::RequestRedirectMode::Error, + ] { + assert!( + raw_subresource_memory_cache_key(&per_hop.clone().with_redirect_mode(mode)) + .is_none() + ); + } + } + #[test] fn cancelling_one_shared_script_consumer_preserves_its_sibling() { let load = ScriptTextLoad::pending(); diff --git a/moli-renderer-v8/src/network/devtools_resource_load.rs b/moli-renderer-v8/src/network/devtools_resource_load.rs index 9e7d2202d2..997a58a4a9 100644 --- a/moli-renderer-v8/src/network/devtools_resource_load.rs +++ b/moli-renderer-v8/src/network/devtools_resource_load.rs @@ -99,6 +99,7 @@ impl RendererNetworkResourceLoadResponse { moli_fetch::ResponseHead { final_url: self.final_url.clone(), status: self.status, + status_text: None, headers: self.headers.clone(), request_cookie_report: self.request_cookie_report.clone(), cookie_set_reports: self.cookie_set_reports.clone(), diff --git a/moli-renderer-v8/src/network_host.rs b/moli-renderer-v8/src/network_host.rs index bf0d22c446..e53818d72a 100644 --- a/moli-renderer-v8/src/network_host.rs +++ b/moli-renderer-v8/src/network_host.rs @@ -152,37 +152,19 @@ pub(crate) use self::response::{ build_fetch_response_object_from_subresource_body_for_request_mode_with_filter, build_filtered_cached_response_object, build_navigation_preload_response_object_from_stream_for_request_mode, - cors_preflight_request_headers, - cors_preflight_request_headers_for_origin, - cors_request_origin_after_redirects, - fetch_response_needs_orb_body_validation, - filter_cors_exposed_response_headers, - filter_cors_exposed_response_headers_for_origin, - is_cors_policy_failure_message, - materialize_response_object_body, - materialize_response_object_body_with_chunk_callback, - materialized_body_bytes_from_value, - network_response_filter, - response_constructor_callback, - validate_cors_preflight_response, - validate_cors_preflight_response_for_origin, - validate_cors_response_chain, - validate_cors_response_chain_for_origin, - response_has_null_body, - materialize_response_object_internal_head, - set_filtered_response_internal_head, - materialize_cache_response_object_head, - validate_cors_response_for_origin, - validate_cross_origin_embedder_and_document_isolation_policy, - validate_cross_origin_resource_policy, - validate_fetch_response_headers_for_origin, - validate_fetch_response_security_policy, - validate_fetch_response_security_policy_for_origin, + cors_preflight_request_headers, cors_preflight_request_headers_for_origin, + fetch_response_needs_orb_body_validation, filter_cors_exposed_response_headers_for_origin, + is_cors_policy_failure_message, materialize_cache_response_object_head, + materialize_response_object_body, materialize_response_object_body_with_chunk_callback, + materialize_response_object_internal_head, materialized_body_bytes_from_value, + network_response_filter, response_constructor_callback, response_has_null_body, + set_filtered_response_internal_head, validate_cors_preflight_response, + validate_cors_response_chain, validate_cross_origin_embedder_and_document_isolation_policy, + validate_cross_origin_resource_policy, validate_fetch_response_headers_for_origin, + validate_fetch_response_security_policy, validate_fetch_response_security_policy_for_origin, validate_fetch_response_security_policy_with_body, validate_fetch_response_security_policy_with_body_classified_for_origin, - validate_fetch_response_security_policy_with_body_for_origin, - validate_opaque_response_blocking_with_body, - validated_opaque_response_body, + validate_fetch_response_security_policy_with_body_for_origin, validated_opaque_response_body, }; #[cfg(test)] pub(crate) use self::response::{materialize_response_object, materialize_response_object_head}; diff --git a/moli-renderer-v8/src/network_host/async_fetch.rs b/moli-renderer-v8/src/network_host/async_fetch.rs index 40c7111d63..22b139924c 100644 --- a/moli-renderer-v8/src/network_host/async_fetch.rs +++ b/moli-renderer-v8/src/network_host/async_fetch.rs @@ -128,6 +128,7 @@ fn browser_request_needs_cors_redirect_checks(request: &Request) -> bool { | BrowserRequestMetadata::EventSource | BrowserRequestMetadata::JsonModule | BrowserRequestMetadata::Manifest + | BrowserRequestMetadata::Script | BrowserRequestMetadata::StyleModule | BrowserRequestMetadata::Xhr, ) @@ -198,7 +199,9 @@ impl ManualCorsRedirectState { ) -> Result { // Redirect request records describe the next hop's cookie decision, // just as they do when the transport follows redirects internally. - if let Some(previous) = self.request.redirect_chain_mut().last_mut() { + if let Some(previous) = self.request.redirect_chain_mut().last_mut() + && previous.source == moli_fetch::RedirectSource::Network + { previous.request_cookie_report = head.request_cookie_report.clone(); previous.request_extra_info = request_extra_info.cloned(); } @@ -236,7 +239,8 @@ impl ManualCorsRedirectState { .password() .is_some_and(|password| !password.is_empty())) && self.request.request_origin().is_some_and(|origin| { - self.request.has_cross_origin_url(&head.final_url) || !origin.same_origin_url(&next_url) + self.request.has_cross_origin_url(&head.final_url) + || !origin.same_origin_url(&next_url) }) { return Err("CORS redirect URL must not include credentials".to_owned()); diff --git a/moli-renderer-v8/src/network_host/response.rs b/moli-renderer-v8/src/network_host/response.rs index 2c328d4159..64695a0248 100644 --- a/moli-renderer-v8/src/network_host/response.rs +++ b/moli-renderer-v8/src/network_host/response.rs @@ -18,30 +18,16 @@ pub(in crate::network_host) use self::bindings::{ParsedResponseInit, parse_respo pub(crate) use self::bindings::{build_error_response_object, response_constructor_callback}; pub(super) use self::body_methods::install_response_body_methods; pub(crate) use self::cors::{ - FetchResponseSecurityViolation, - cors_preflight_request_headers, - cors_preflight_request_headers_for_origin, - cors_request_origin_after_redirects, - fetch_response_needs_orb_body_validation, - filter_cors_exposed_response_headers, - filter_cors_exposed_response_headers_for_origin, - is_cors_policy_failure_message, - validate_cors_preflight_response, - validate_cors_preflight_response_for_origin, - validate_cors_response_chain, - validate_cors_response_chain_for_origin, - validate_cors_response_for_origin, - validate_cors_response_headers, + FetchResponseSecurityViolation, cors_preflight_request_headers, + cors_preflight_request_headers_for_origin, fetch_response_needs_orb_body_validation, + filter_cors_exposed_response_headers_for_origin, is_cors_policy_failure_message, + validate_cors_preflight_response, validate_cors_response_chain, validate_cors_response_headers, validate_cross_origin_embedder_and_document_isolation_policy, - validate_cross_origin_resource_policy, - validate_fetch_response_headers_for_origin, - validate_fetch_response_security_policy, - validate_fetch_response_security_policy_for_origin, + validate_cross_origin_resource_policy, validate_fetch_response_headers_for_origin, + validate_fetch_response_security_policy, validate_fetch_response_security_policy_for_origin, validate_fetch_response_security_policy_with_body, validate_fetch_response_security_policy_with_body_classified_for_origin, - validate_fetch_response_security_policy_with_body_for_origin, - validate_opaque_response_blocking_with_body, - validated_opaque_response_body, + validate_fetch_response_security_policy_with_body_for_origin, validated_opaque_response_body, }; pub(crate) use self::materialize::{ FetchResponseRequest, MaterializedResponseBody, MaterializedResponseHead, @@ -54,8 +40,7 @@ pub(crate) use self::materialize::{ materialize_cache_response_object_head, materialize_response_object_body, materialize_response_object_body_with_chunk_callback, materialize_response_object_internal_head, materialized_body_bytes_from_value, - set_filtered_response_internal_head, - network_response_filter, + network_response_filter, set_filtered_response_internal_head, }; #[cfg(test)] pub(crate) use self::materialize::{materialize_response_object, materialize_response_object_head}; diff --git a/moli-renderer-v8/src/network_host/response/cors.rs b/moli-renderer-v8/src/network_host/response/cors.rs index 7ff1b89baf..24262ca14f 100644 --- a/moli-renderer-v8/src/network_host/response/cors.rs +++ b/moli-renderer-v8/src/network_host/response/cors.rs @@ -38,20 +38,6 @@ pub(crate) fn is_cors_policy_failure_message(message: &str) -> bool { message.contains("CORS check failed:") || message.contains("CORS preflight failed:") } -pub(crate) fn cors_request_origin_after_redirects<'a>( - request_origin: &WebOrigin, - redirects: impl IntoIterator, -) -> WebOrigin { - if redirects - .into_iter() - .any(|(from, to)| !same_origin(from, to) && !request_origin.same_origin_url(from)) - { - WebOrigin::Opaque - } else { - request_origin.clone() - } -} - /// Validates an already fetched network response and its network redirects. /// Service worker and browser-generated redirects still contribute to taint, /// but their synthetic response headers are not subject to the CORS check. @@ -95,7 +81,8 @@ pub(crate) fn validate_cors_response_chain_for_origin( validate_cors_response_chain(request_origin, head, credentials_mode) } -pub(crate) fn validate_cors_response_for_origin( +#[cfg(test)] +fn validate_cors_response_for_origin( request_origin: &WebOrigin, response_url: &url::Url, response_headers: &[(String, String)], @@ -106,7 +93,11 @@ pub(crate) fn validate_cors_response_for_origin( { return Ok(()); } - validate_cors_response_headers(request_origin.ascii_serialization(), response_headers, credentials_mode) + validate_cors_response_headers( + request_origin.ascii_serialization(), + response_headers, + credentials_mode, + ) } pub(crate) fn validate_fetch_response_security_policy( @@ -271,8 +262,15 @@ pub(crate) fn validate_fetch_response_security_policy_with_body_for_origin( policy_context: crate::types::SubresourcePolicyContext, ) -> Result<(), String> { validate_fetch_response_security_policy_with_body_classified_for_origin( - document_url, request_origin, head, response_body, request_mode, credentials_mode, policy_context, - ).map_err(FetchResponseSecurityViolation::into_message) + document_url, + request_origin, + head, + response_body, + request_mode, + credentials_mode, + policy_context, + ) + .map_err(FetchResponseSecurityViolation::into_message) } pub(crate) fn validate_fetch_response_security_policy_with_body_classified_for_origin( @@ -518,7 +516,13 @@ pub(crate) fn cors_preflight_request_headers_for_origin( request_headers: &[(String, String)], use_cors_preflight: bool, ) -> Option> { - cors_preflight_request_headers(!request_origin.same_origin_url(request_url), request_url, method, request_headers, use_cors_preflight) + cors_preflight_request_headers( + !request_origin.same_origin_url(request_url), + request_url, + method, + request_headers, + use_cors_preflight, + ) } pub(crate) fn validate_cors_preflight_response( @@ -595,7 +599,8 @@ pub(crate) fn validate_cors_preflight_response( Ok(()) } -pub(crate) fn validate_cors_preflight_response_for_origin( +#[cfg(test)] +fn validate_cors_preflight_response_for_origin( request_origin: &WebOrigin, credentials_mode: RequestCredentialsMode, requested_method: &str, @@ -604,7 +609,15 @@ pub(crate) fn validate_cors_preflight_response_for_origin( response_headers: &[(String, String)], use_cors_preflight: bool, ) -> Result<(), String> { - validate_cors_preflight_response(request_origin.ascii_serialization(), credentials_mode, requested_method, request_headers, response_status, response_headers, use_cors_preflight) + validate_cors_preflight_response( + request_origin.ascii_serialization(), + credentials_mode, + requested_method, + request_headers, + response_status, + response_headers, + use_cors_preflight, + ) } pub(crate) fn filter_cors_exposed_response_headers_for_origin( @@ -721,6 +734,7 @@ mod tests { moli_fetch::ResponseHead { final_url, status: 200, + status_text: None, headers, request_cookie_report: None, cookie_set_reports: Vec::new(), @@ -737,6 +751,7 @@ mod tests { let mut head = moli_fetch::ResponseHead { final_url: url("https://final.test/script.js"), status: 200, + status_text: None, headers: vec![("Access-Control-Allow-Origin".to_owned(), "*".to_owned())], request_cookie_report: None, cookie_set_reports: Vec::new(), @@ -864,12 +879,24 @@ mod tests { RequestCredentialsMode::Include, ] { assert_eq!( - validate_cors_response_for_origin(&WebOrigin::from_url(&document_url), &response_url, &headers, mode).is_ok(), + validate_cors_response_for_origin( + &WebOrigin::from_url(&document_url), + &response_url, + &headers, + mode + ) + .is_ok(), mode != RequestCredentialsMode::Include || values == ["true"], "mode={mode:?}, values={values:?}" ); assert!( - validate_cors_response_for_origin(&WebOrigin::from_url(&document_url), &document_url, &headers, mode).is_ok(), + validate_cors_response_for_origin( + &WebOrigin::from_url(&document_url), + &document_url, + &headers, + mode + ) + .is_ok(), "same-origin responses do not require CORS permission" ); } @@ -881,36 +908,75 @@ mod tests { let home = url("https://page.test/a"); let away = url("https://script.test/b"); let elsewhere = url("https://other.test/c"); - let origin = WebOrigin::from_url(&home); - assert_eq!( - cors_request_origin_after_redirects(&origin, [(&home, &away)]), - origin - ); - for redirects in [ - vec![(&away, &elsewhere)], - vec![(&home, &away), (&away, &home)], - ] { - let tainted = cors_request_origin_after_redirects(&origin, redirects); - assert!(tainted.is_opaque()); - assert!( - validate_cors_response_for_origin( - &tainted, - &home, - &[("access-control-allow-origin".into(), "null".into())], - RequestCredentialsMode::SameOrigin, - ) - .is_ok() - ); - assert!( - validate_cors_response_for_origin( - &tainted, - &home, - &[], - RequestCredentialsMode::SameOrigin, - ) - .is_err(), - "returning to the initial origin must not bypass CORS" - ); + for origin in [WebOrigin::from_url(&home), WebOrigin::Opaque] { + for source in [ + RedirectSource::Network, + RedirectSource::ServiceWorker, + RedirectSource::Internal, + ] { + for (urls, tuple_origin) in [ + (vec![&home, &away], "https://page.test"), + (vec![&away, &elsewhere], "null"), + (vec![&home, &away, &home], "null"), + ] { + let expected_origin = if origin.is_opaque() { + "null" + } else { + tuple_origin + }; + let mut head = header_response( + (*urls.last().unwrap()).clone(), + vec![("Access-Control-Allow-Origin".into(), expected_origin.into())], + ); + head.redirect_chain = urls + .windows(2) + .map(|pair| moli_fetch::RedirectInfo { + source, + from_url: pair[0].clone(), + to_url: pair[1].clone(), + status: 302, + // The response to each hop is checked before its redirect + // changes the serialized origin for the following request. + headers: if source == RedirectSource::Network { + vec![( + "Access-Control-Allow-Origin".into(), + origin.ascii_serialization().into(), + )] + } else { + Vec::new() + }, + network_extra_info_available: false, + request_extra_info: None, + response_extra_info: None, + redirect_has_extra_info: false, + request_cookie_report: None, + cookie_set_reports: Vec::new(), + from_cache: false, + negotiated_http_version: None, + }) + .collect(); + assert_eq!( + head.url_list().serialized_origin_for_origin(&origin), + expected_origin + ); + assert!(head.url_list().has_cross_origin_url_for_origin(&origin)); + validate_cors_response_chain_for_origin( + &origin, + &head, + RequestCredentialsMode::SameOrigin, + ) + .expect("authorize network hops using their individual request origins"); + head.headers.clear(); + validate_cors_response_chain_for_origin( + &origin, + &head, + RequestCredentialsMode::SameOrigin, + ) + .expect_err( + "returning to the client origin cannot bypass final CORS validation", + ); + } + } } } @@ -927,8 +993,13 @@ mod tests { ("X-Other".to_owned(), "ok".to_owned()), ]; - let preflight = - cors_preflight_request_headers(true, &url("http://other.test/data"), "PUT", &headers, false); + let preflight = cors_preflight_request_headers( + true, + &url("http://other.test/data"), + "PUT", + &headers, + false, + ); assert_eq!( preflight, @@ -954,7 +1025,13 @@ mod tests { ]; assert_eq!( - cors_preflight_request_headers(true, &url("http://other.test/data"), "POST", &headers, false), + cors_preflight_request_headers( + true, + &url("http://other.test/data"), + "POST", + &headers, + false + ), None ); } diff --git a/moli-renderer-v8/src/planning.rs b/moli-renderer-v8/src/planning.rs index d0c156fc32..a7e961a7f7 100644 --- a/moli-renderer-v8/src/planning.rs +++ b/moli-renderer-v8/src/planning.rs @@ -416,7 +416,7 @@ pub(crate) async fn load_service_worker_aware_external_script_source_outcome( ClassicScriptResponseProvenance::Network } else { ClassicScriptResponseProvenance::ServiceWorker { - filter: response.response_filter, + filter: response.response_filter.clone(), } }; external_script_source_load_outcome_from_response_inner( @@ -586,8 +586,12 @@ pub(crate) fn external_script_source_load_outcome_from_response( ) -> PreparedScriptSourceLoadOutcome { let request_mode = external_script_request_mode(script.kind, &script.fetch_metadata); let head = response.head(); - let response_filter = - crate::network_host::network_response_filter(&script.initiator_url, &head, request_mode, moli_fetch::RequestRedirectMode::Follow); + let response_filter = crate::network_host::network_response_filter( + &script.initiator_url, + &head, + request_mode, + moli_fetch::RequestRedirectMode::Follow, + ); let cors_error = if request_mode == RequestMode::Cors { crate::network_host::validate_cors_response_chain( &script.initiator_url, diff --git a/moli-renderer-v8/src/script_vm/subresource_fetch.rs b/moli-renderer-v8/src/script_vm/subresource_fetch.rs index 7ee9c9f2a2..c19ec3b273 100644 --- a/moli-renderer-v8/src/script_vm/subresource_fetch.rs +++ b/moli-renderer-v8/src/script_vm/subresource_fetch.rs @@ -3721,8 +3721,9 @@ impl ScriptVm { trace_fields, record_started, ); + let request_origin = pending.request_origin(); let mut observable_response = response; - if pending.info.resource_type == SubresourceResourceType::Xhr && !response_filter.is_some_and(|filter| filter.is_readable()) { + if pending.info.resource_type == SubresourceResourceType::Xhr && !response_filter.as_ref().is_some_and(|filter| filter.is_readable()) { observable_response.headers = crate::network_host::filter_cors_exposed_response_headers( &pending.request_origin, @@ -3747,7 +3748,7 @@ impl ScriptVm { }; let response_filter = response_filter.or_else(|| Some(response_request.network_response_filter( &pending.info.document_url, - &pending.request_origin(), + &request_origin, &head, pending.credentials_mode, ))); @@ -4881,7 +4882,7 @@ impl ScriptVm { } let mut observable_head = started.head.clone(); - if pending.info.resource_type == SubresourceResourceType::Xhr && !started.response_filter.is_some_and(|filter| filter.is_readable()) { + if pending.info.resource_type == SubresourceResourceType::Xhr && !started.response_filter.as_ref().is_some_and(|filter| filter.is_readable()) { observable_head.headers = crate::network_host::filter_cors_exposed_response_headers_for_origin( &pending.request_origin(), &observable_head, @@ -5852,9 +5853,7 @@ impl ScriptVm { trace_fields, record_started, ); - let request_origin = streaming - .pending - .response_request_origin(streaming.head.redirect_chain.iter().map(|redirect| (&redirect.from_url, &redirect.to_url))); + let request_origin = streaming.pending.request_origin(); if let PendingSubresourceContinuation::Xhr { xhr, .. } = streaming.pending.continuation && let Some(response_body) = xhr_delivery_body diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs index 3db3fa9a57..e30403519d 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/xhr.rs @@ -1284,6 +1284,7 @@ fn xml_http_request_default_response_type_parses_response_xml_for_document_mime( #[test] fn xml_http_request_response_document_uses_response_url_and_requester_origin() { + let environment = moli_v8_platform::ProcessEnvironmentOwner::default(); for child_realm in [false, true] { for (mime, response_type) in [ ("application/xml", ""), @@ -1294,8 +1295,7 @@ fn xml_http_request_response_document_uses_response_url_and_requester_origin() { "https://requester.example/page/index.html", "", ); - vm.set_timezone_override_and_sync_surface(Some("UTC")) - .unwrap(); + environment.set_timezone(Some("UTC")).unwrap(); vm.eval(&format!( r#"(() => {{ const frame = document.createElement('iframe'); @@ -1512,6 +1512,7 @@ fn xml_http_request_document_response_requires_an_eligible_mime_and_well_formed_ #[test] fn xhr_streamed_response_documents_keep_distinct_source_modification_times() { + let environment = moli_v8_platform::ProcessEnvironmentOwner::default(); for (mime, response_type) in [ ("application/xml", ""), ("application/xml", "document"), @@ -1520,8 +1521,7 @@ fn xhr_streamed_response_documents_keep_distinct_source_modification_times() { let mut vm = new_storage_test_vm("https://xhr-document-modified.test/"); vm.document_runtime .set_document_source_last_modified(Some(5_025_000.0)); - vm.set_timezone_override_and_sync_surface(Some("UTC")) - .unwrap(); + environment.set_timezone(Some("UTC")).unwrap(); vm.set_fetch_subresource_interception( true, Some(crate::types::SubresourceResourceType::Xhr), @@ -1633,8 +1633,7 @@ fn xhr_streamed_response_documents_keep_distinct_source_modification_times() { .unwrap(), r#"["11/06/1994 08:49:37","01/01/1970 00:00:00","01/01/1970 01:23:45",true]"# ); - vm.set_timezone_override_and_sync_surface(Some("Asia/Shanghai")) - .unwrap(); + environment.set_timezone(Some("Asia/Shanghai")).unwrap(); assert_eq!( vm.eval("__responseDocuments[0].lastModified").unwrap(), "11/06/1994 16:49:37" diff --git a/moli-renderer-v8/src/types.rs b/moli-renderer-v8/src/types.rs index 565c212c9e..c31097fc31 100644 --- a/moli-renderer-v8/src/types.rs +++ b/moli-renderer-v8/src/types.rs @@ -561,17 +561,6 @@ impl PendingSubresourceFetchState { self.request_origin.clone() } - pub(super) fn response_request_origin<'a>( - &self, - redirects: impl IntoIterator, - ) -> moli_url::WebOrigin { - let origin = self.request_origin(); - if self.request_mode != moli_fetch::RequestMode::Cors { - return origin; - } - crate::network_host::cors_request_origin_after_redirects(&origin, redirects) - } - pub(super) fn detach_keepalive_window_fetch(&mut self) -> bool { let PendingSubresourceExecutionContext::WindowFetch(context) = &self.execution_context else { diff --git a/moli-renderer-v8/src/worker/global_scope/import_scripts.rs b/moli-renderer-v8/src/worker/global_scope/import_scripts.rs index b5a075d736..f3827844c1 100644 --- a/moli-renderer-v8/src/worker/global_scope/import_scripts.rs +++ b/moli-renderer-v8/src/worker/global_scope/import_scripts.rs @@ -240,8 +240,7 @@ pub(super) fn fetch_worker_import_source_blocking( validate_fetch_response_security_policy_for_origin( initiator_url, &WebOrigin::from_url(initiator_url), - &response.final_url, - &response.headers, + &response.head(), RequestMode::NoCors, RequestCredentialsMode::SameOrigin, policy_context, diff --git a/moli-renderer-v8/src/worker/global_scope/mod.rs b/moli-renderer-v8/src/worker/global_scope/mod.rs index 23151f4aa9..9aead11439 100644 --- a/moli-renderer-v8/src/worker/global_scope/mod.rs +++ b/moli-renderer-v8/src/worker/global_scope/mod.rs @@ -72,8 +72,7 @@ use crate::network_host::{ XHR_SEND_FLAG_SLOT, XHR_TIMEOUT_SLOT, XHR_TIMEOUT_START_MS_SLOT, XHR_TIMEOUT_TIMER_SLOT, XHR_URL_SLOT, XHR_WITH_CREDENTIALS_SLOT, append_default_body_content_type, apply_xhr_failure, apply_xhr_response, apply_xhr_response_body_source, apply_xhr_timeout, apply_xhr_upload_event, - capture_xhr_upload_listener_flag, close_pending_network_body_stream, - cors_request_origin_after_redirects, dispatch_xhr_loadstart, + capture_xhr_upload_listener_flag, close_pending_network_body_stream, dispatch_xhr_loadstart, enqueue_pending_network_body_chunk, error_pending_network_body_stream_with_reason, extract_subresource_auth_challenge, fetch_browser_subresource_raw_stream_with_preflight_headers_and_network_metadata, @@ -82,8 +81,7 @@ use crate::network_host::{ is_cors_policy_failure_message, parse_fetch_init, prepare_xhr_send_body_from_args, request_input_snapshot, request_object_credentials_mode, resolve_context_url, set_xhr_state_bool, set_xhr_state_number, throw_synchronous_xhr_failure, - validate_cors_response_for_origin, validate_fetch_response_headers_for_origin, - validate_fetch_response_security_policy_for_origin, + validate_fetch_response_headers_for_origin, validate_fetch_response_security_policy_for_origin, validate_fetch_response_security_policy_with_body_classified_for_origin, xhr_author_request_headers, xhr_ensure_send_allowed, xhr_state_bool_property, xhr_state_number_property, xhr_state_string_property, diff --git a/moli-renderer-v8/src/worker/global_scope/xhr.rs b/moli-renderer-v8/src/worker/global_scope/xhr.rs index 09da1e6ecc..72c1893929 100644 --- a/moli-renderer-v8/src/worker/global_scope/xhr.rs +++ b/moli-renderer-v8/src/worker/global_scope/xhr.rs @@ -718,6 +718,7 @@ fn send_synchronous_worker_xhr( throw_synchronous_xhr_failure(scope, xhr, &request_url_text, "NetworkError"); return; } + let request_origin = WebOrigin::from_url(&prepared.document_url); record_worker_subresource_success( &state.borrow(), prepared.document_url, @@ -731,8 +732,7 @@ fn send_synchronous_worker_xhr( ); response.headers = filter_cors_exposed_response_headers_for_origin( &request_origin, - &response.final_url, - &response.headers, + &response.head(), prepared.credentials_mode, ); apply_xhr_response(scope, xhr, response); diff --git a/moli-renderer-v8/src/worker/thread/mod.rs b/moli-renderer-v8/src/worker/thread/mod.rs index 074075c2a4..78b9b1bf96 100644 --- a/moli-renderer-v8/src/worker/thread/mod.rs +++ b/moli-renderer-v8/src/worker/thread/mod.rs @@ -672,6 +672,7 @@ fn start_worker_module_graph_fetch( .with_credentials_mode(request.credentials_mode()); let requested_url = request.url().clone(); let request_initiator_url = request.initiator_url().clone(); + let request_credentials_mode = request.credentials_mode(); let requested_module_type = request.module_type().map(str::to_owned); let requested_kind = request.kind(); let response_content_security_policies = content_security_policies.clone();