diff options
Diffstat (limited to 'components/net/fetch/methods.rs')
-rw-r--r-- | components/net/fetch/methods.rs | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 697a46fedda..b1ad01b81e0 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -23,8 +23,8 @@ use net_traits::http_status::HttpStatus; use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer}; use net_traits::request::{ BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator, - InsecureRequestsPolicy, Origin, RedirectMode, Referrer, Request, RequestMode, ResponseTainting, - Window, is_cors_safelisted_method, is_cors_safelisted_request_header, + InsecureRequestsPolicy, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestMode, + ResponseTainting, Window, is_cors_safelisted_method, is_cors_safelisted_request_header, }; use net_traits::response::{Response, ResponseBody, ResponseType}; use net_traits::{ @@ -42,7 +42,7 @@ use crate::fetch::cors_cache::CorsCache; use crate::fetch::headers::determine_nosniff; use crate::filemanager_thread::FileManager; use crate::http_loader::{HttpState, determine_requests_referrer, http_fetch, set_default_accept}; -use crate::protocols::ProtocolRegistry; +use crate::protocols::{ProtocolRegistry, is_url_potentially_trustworthy}; use crate::request_interceptor::RequestInterceptor; use crate::subresource_integrity::is_response_integrity_valid; @@ -169,6 +169,29 @@ pub async fn fetch_with_cors_cache( // TODO: We don't implement fetchParams as defined in the spec } +fn convert_request_to_csp_request(request: &Request, origin: &ImmutableOrigin) -> csp::Request { + csp::Request { + url: request.url().into_url(), + origin: origin.clone().into_url_origin(), + redirect_count: request.redirect_count, + destination: request.destination, + initiator: match request.initiator { + Initiator::Download => csp::Initiator::Download, + Initiator::ImageSet => csp::Initiator::ImageSet, + Initiator::Manifest => csp::Initiator::Manifest, + Initiator::Prefetch => csp::Initiator::Prefetch, + _ => csp::Initiator::None, + }, + nonce: request.cryptographic_nonce_metadata.clone(), + integrity_metadata: request.integrity_metadata.clone(), + parser_metadata: match request.parser_metadata { + ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted, + ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted, + ParserMetadata::Default => csp::ParserMetadata::None, + }, + } +} + /// <https://www.w3.org/TR/CSP/#should-block-request> pub fn should_request_be_blocked_by_csp( request: &Request, @@ -178,17 +201,7 @@ pub fn should_request_be_blocked_by_csp( Origin::Client => return (csp::CheckResult::Allowed, Vec::new()), Origin::Origin(origin) => origin, }; - - let csp_request = csp::Request { - url: request.url().into_url(), - origin: origin.clone().into_url_origin(), - redirect_count: request.redirect_count, - destination: request.destination, - initiator: csp::Initiator::None, - nonce: request.cryptographic_nonce_metadata.clone(), - integrity_metadata: request.integrity_metadata.clone(), - parser_metadata: csp::ParserMetadata::None, - }; + let csp_request = convert_request_to_csp_request(request, origin); policy_container .csp_list @@ -197,6 +210,24 @@ pub fn should_request_be_blocked_by_csp( .unwrap_or((csp::CheckResult::Allowed, Vec::new())) } +/// <https://www.w3.org/TR/CSP/#report-for-request> +pub fn report_violations_for_request_by_csp( + request: &Request, + policy_container: &PolicyContainer, +) -> Vec<csp::Violation> { + let origin = match &request.origin { + Origin::Client => return Vec::new(), + Origin::Origin(origin) => origin, + }; + let csp_request = convert_request_to_csp_request(request, origin); + + policy_container + .csp_list + .as_ref() + .map(|c| c.report_violations_for_request(&csp_request)) + .unwrap_or_default() +} + /// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch) pub async fn main_fetch( fetch_params: &mut FetchParams, @@ -232,9 +263,6 @@ pub async fn main_fetch( ))); } - // Step 2.2. - // TODO: Report violations. - // The request should have a valid policy_container associated with it. // TODO: This should not be `Client` here let policy_container = match &request.policy_container { @@ -242,12 +270,19 @@ pub async fn main_fetch( RequestPolicyContainer::PolicyContainer(container) => container.to_owned(), }; + // Step 2.2. + let violations = report_violations_for_request_by_csp(request, &policy_container); + + if !violations.is_empty() { + target.process_csp_violations(request, violations); + } + // Step 3. // TODO: handle request abort. // Step 4. Upgrade request to a potentially trustworthy URL, if appropriate. if should_upgrade_request_to_potentially_trustworty(request, context) || - should_upgrade_mixed_content_request(request) + should_upgrade_mixed_content_request(request, &context.protocols) { trace!( "upgrading {} targeting {:?}", @@ -294,7 +329,7 @@ pub async fn main_fetch( "Request attempted on bad port".into(), ))); } - if should_request_be_blocked_as_mixed_content(request) { + if should_request_be_blocked_as_mixed_content(request, &context.protocols) { response = Some(Response::network_error(NetworkError::Internal( "Blocked as mixed content".into(), ))); @@ -359,13 +394,16 @@ pub async fn main_fetch( if (same_origin && request.response_tainting == ResponseTainting::Basic) || // request's current URL's scheme is "data" current_scheme == "data" || + // Note: Although it is not part of the specification, we make an exception here + // for custom protocols that are explicitly marked as active for fetch. + context.protocols.is_fetchable(current_scheme) || // request's mode is "navigate" or "websocket" matches!( request.mode, RequestMode::Navigate | RequestMode::WebSocket { .. } ) { - // Substep 1. Set request’s response tainting to "basic". + // Substep 1. Set request's response tainting to "basic". request.response_tainting = ResponseTainting::Basic; // Substep 2. Return the result of running scheme fetch given fetchParams. @@ -373,13 +411,13 @@ pub async fn main_fetch( } else if request.mode == RequestMode::SameOrigin { Response::network_error(NetworkError::Internal("Cross-origin response".into())) } else if request.mode == RequestMode::NoCors { - // Substep 1. If request’s redirect mode is not "follow", then return a network error. + // Substep 1. If request's redirect mode is not "follow", then return a network error. if request.redirect_mode != RedirectMode::Follow { Response::network_error(NetworkError::Internal( "NoCors requests must follow redirects".into(), )) } else { - // Substep 2. Set request’s response tainting to "opaque". + // Substep 2. Set request's response tainting to "opaque". request.response_tainting = ResponseTainting::Opaque; // Substep 3. Return the result of running scheme fetch given fetchParams. @@ -490,7 +528,7 @@ pub async fn main_fetch( let should_replace_with_mime_type_error = !response_is_network_error && should_be_blocked_due_to_mime_type(request.destination, &response.headers); let should_replace_with_mixed_content = !response_is_network_error && - should_response_be_blocked_as_mixed_content(request, &response); + should_response_be_blocked_as_mixed_content(request, &response, &context.protocols); // Step 15. let mut network_error_response = response @@ -933,7 +971,10 @@ pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool { } /// <https://w3c.github.io/webappsec-mixed-content/#should-block-fetch> -pub fn should_request_be_blocked_as_mixed_content(request: &Request) -> bool { +pub fn should_request_be_blocked_as_mixed_content( + request: &Request, + protocol_registry: &ProtocolRegistry, +) -> bool { // Step 1. Return allowed if one or more of the following conditions are met: // 1.1. Does settings prohibit mixed security contexts? // returns "Does Not Restrict Mixed Security Contexts" when applied to request’s client. @@ -944,7 +985,7 @@ pub fn should_request_be_blocked_as_mixed_content(request: &Request) -> bool { } // 1.2. request’s URL is a potentially trustworthy URL. - if request.url().is_potentially_trustworthy() { + if is_url_potentially_trustworthy(protocol_registry, &request.url()) { return false; } @@ -961,7 +1002,11 @@ pub fn should_request_be_blocked_as_mixed_content(request: &Request) -> bool { } /// <https://w3c.github.io/webappsec-mixed-content/#should-block-response> -pub fn should_response_be_blocked_as_mixed_content(request: &Request, response: &Response) -> bool { +pub fn should_response_be_blocked_as_mixed_content( + request: &Request, + response: &Response, + protocol_registry: &ProtocolRegistry, +) -> bool { // Step 1. Return allowed if one or more of the following conditions are met: // 1.1. Does settings prohibit mixed security contexts? returns Does Not Restrict Mixed Content // when applied to request’s client. @@ -975,7 +1020,7 @@ pub fn should_response_be_blocked_as_mixed_content(request: &Request, response: if response .actual_response() .url() - .is_some_and(|response_url| response_url.is_potentially_trustworthy()) + .is_some_and(|response_url| is_url_potentially_trustworthy(protocol_registry, response_url)) { return false; } @@ -1041,7 +1086,7 @@ fn should_upgrade_request_to_potentially_trustworty( // request’s header list if any of the following criteria are met: // * request’s URL is not a potentially trustworthy URL // * request’s URL's host is not a preloadable HSTS host - if !request.current_url().is_potentially_trustworthy() || + if !is_url_potentially_trustworthy(&context.protocols, &request.current_url()) || !request.current_url().host_str().is_some_and(|host| { !context.state.hsts_list.read().unwrap().is_host_secure(host) }) @@ -1094,10 +1139,13 @@ fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecur } /// <https://w3c.github.io/webappsec-mixed-content/#upgrade-algorithm> -fn should_upgrade_mixed_content_request(request: &Request) -> bool { +fn should_upgrade_mixed_content_request( + request: &Request, + protocol_registry: &ProtocolRegistry, +) -> bool { let url = request.url(); // Step 1.1 : request’s URL is a potentially trustworthy URL. - if url.is_potentially_trustworthy() { + if is_url_potentially_trustworthy(protocol_registry, &url) { return false; } |