diff options
Diffstat (limited to 'components/net/fetch/methods.rs')
-rw-r--r-- | components/net/fetch/methods.rs | 119 |
1 files changed, 108 insertions, 11 deletions
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 8c5758830c1..49b063f1bc8 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -34,7 +34,7 @@ use net_traits::{ use rustls_pki_types::CertificateDer; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; -use servo_url::{Host, ServoUrl}; +use servo_url::{Host, ImmutableOrigin, ServoUrl}; use tokio::sync::mpsc::{UnboundedReceiver as TokioReceiver, UnboundedSender as TokioSender}; use super::fetch_params::FetchParams; @@ -278,7 +278,6 @@ pub async fn main_fetch( // Step 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. - // TODO: check "should fetching request be blocked as mixed content" if should_request_be_blocked_by_csp(request, &policy_container) == csp::CheckResult::Blocked { warn!("Request blocked by CSP"); response = Some(Response::network_error(NetworkError::Internal( @@ -290,6 +289,11 @@ pub async fn main_fetch( "Request attempted on bad port".into(), ))); } + if should_request_be_blocked_as_mixed_content(request) { + response = Some(Response::network_error(NetworkError::Internal( + "Blocked as mixed content".into(), + ))); + } // Step 8: If request’s referrer policy is the empty string, then set request’s referrer policy // to request’s policy container’s referrer policy. @@ -480,6 +484,8 @@ pub async fn main_fetch( should_be_blocked_due_to_nosniff(request.destination, &response.headers); 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); // Step 15. let mut network_error_response = response @@ -502,7 +508,7 @@ pub async fn main_fetch( } // Step 19. If response is not a network error and any of the following returns blocked - // TODO: * should internalResponse to request be blocked as mixed content + // * should internalResponse to request be blocked as mixed content // TODO: * should internalResponse to request be blocked by Content Security Policy // * should internalResponse to request be blocked due to its MIME type // * should internalResponse to request be blocked due to nosniff @@ -518,6 +524,10 @@ pub async fn main_fetch( blocked_error_response = Response::network_error(NetworkError::Internal("Blocked by mime type".into())); &blocked_error_response + } else if should_replace_with_mixed_content { + blocked_error_response = + Response::network_error(NetworkError::Internal("Blocked as mixed content".into())); + &blocked_error_response } else { internal_response }; @@ -525,7 +535,10 @@ pub async fn main_fetch( // Step 20. If response’s type is "opaque", internalResponse’s status is 206, internalResponse’s // range-requested flag is set, and request’s header list does not contain `Range`, then set // response and internalResponse to a network error. - let internal_response = if response_type == ResponseType::Opaque && + // Also checking if internal response is a network error to prevent crash from attemtping to + // read status of a network error if we blocked the request above. + let internal_response = if !internal_response.is_network_error() && + response_type == ResponseType::Opaque && internal_response.status.code() == StatusCode::PARTIAL_CONTENT && internal_response.range_requested && !request.headers.contains_key(RANGE) @@ -914,6 +927,66 @@ pub fn should_request_be_blocked_due_to_a_bad_port(url: &ServoUrl) -> bool { false } +/// <https://w3c.github.io/webappsec-mixed-content/#should-block-fetch> +pub fn should_request_be_blocked_as_mixed_content(request: &Request) -> 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. + if do_settings_prohibit_mixed_security_contexts(request) == + MixedSecurityProhibited::NotProhibited + { + return false; + } + + // 1.2. request’s URL is a potentially trustworthy URL. + if request.url().is_potentially_trustworthy() { + return false; + } + + // 1.3. The user agent has been instructed to allow mixed content. + + // 1.4. request’s destination is "document", and request’s target browsing context has + // no parent browsing context. + if request.destination == Destination::Document { + // TODO: request's target browsing context has no parent browsing context + return false; + } + + true +} + +/// <https://w3c.github.io/webappsec-mixed-content/#should-block-response> +pub fn should_response_be_blocked_as_mixed_content(request: &Request, response: &Response) -> 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. + if do_settings_prohibit_mixed_security_contexts(request) == + MixedSecurityProhibited::NotProhibited + { + return false; + } + + // 1.2. response’s url is a potentially trustworthy URL. + if response + .actual_response() + .url() + .is_some_and(|response_url| response_url.is_potentially_trustworthy()) + { + return false; + } + + // 1.3. TODO: The user agent has been instructed to allow mixed content. + + // 1.4. request’s destination is "document", and request’s target browsing context + // has no parent browsing context. + if request.destination == Destination::Document { + // TODO: if requests target browsing context has no parent browsing context + return false; + } + + true +} + /// <https://fetch.spec.whatwg.org/#bad-port> fn is_bad_port(port: u16) -> bool { static BAD_PORTS: [u16; 78] = [ @@ -983,14 +1056,36 @@ fn should_upgrade_request_to_potentially_trustworty( request.insecure_requests_policy == InsecureRequestsPolicy::Upgrade } -// TODO : Needs to revisit +#[derive(Debug, PartialEq)] +pub enum MixedSecurityProhibited { + Prohibited, + NotProhibited, +} + /// <https://w3c.github.io/webappsec-mixed-content/#categorize-settings-object> -fn does_settings_prohibit_mixed_security_contexts(url: &ServoUrl) -> bool { - if url.is_origin_trustworthy() { - return true; +fn do_settings_prohibit_mixed_security_contexts(request: &Request) -> MixedSecurityProhibited { + if let Origin::Origin(ref origin) = request.origin { + // Workers created from a data: url are secure if they were created from secure contexts + let is_origin_data_url_worker = matches!( + *origin, + ImmutableOrigin::Opaque(servo_url::OpaqueOrigin::SecureWorkerFromDataUrl(_)) + ); + + // Step 1. If settings’ origin is a potentially trustworthy origin, + // then return "Prohibits Mixed Security Contexts". + if origin.is_potentially_trustworthy() || is_origin_data_url_worker { + return MixedSecurityProhibited::Prohibited; + } } - false + // Step 2.2. For each navigable navigable in document’s ancestor navigables: + // Step 2.2.1. If navigable’s active document's origin is a potentially trustworthy origin, + // then return "Prohibits Mixed Security Contexts". + if request.has_trustworthy_ancestor_origin { + return MixedSecurityProhibited::Prohibited; + } + + MixedSecurityProhibited::NotProhibited } /// <https://w3c.github.io/webappsec-mixed-content/#upgrade-algorithm> @@ -1008,12 +1103,14 @@ fn should_upgrade_mixed_content_request(request: &Request) -> bool { } // Step 1.3 - if !does_settings_prohibit_mixed_security_contexts(&url) { + if do_settings_prohibit_mixed_security_contexts(request) == + MixedSecurityProhibited::NotProhibited + { return false; } // Step 1.4 : request’s destination is not "image", "audio", or "video". - if matches!( + if !matches!( request.destination, Destination::Audio | Destination::Image | Destination::Video ) { |