mirror of
https://github.com/servo/servo
synced 2026-05-12 18:06:32 +02:00
net: Implement redirect taint for requests (#41824)
This updates the existing algorithm to handle three cases rather than the previous two. The spec was updated afterwards and wasn't reflected in the code yet. The usage of `response.redirect_taint` is in [1], but we don't implement that quite right either. However, postponing that to a future PR to keep things manageable. Part of #41807 [1]: https://fetch.spec.whatwg.org/#fetch-finale Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
committed by
GitHub
parent
3171a4b095
commit
1442e9fbc5
@@ -588,7 +588,7 @@ pub async fn main_fetch(
|
||||
},
|
||||
};
|
||||
|
||||
// Step 13.
|
||||
// Step 13. If recursive is true, then return response.
|
||||
if recursive_flag {
|
||||
return response;
|
||||
}
|
||||
@@ -596,7 +596,7 @@ pub async fn main_fetch(
|
||||
// reborrow request to avoid double mutable borrow
|
||||
let request = &mut fetch_params.request;
|
||||
|
||||
// Step 14.
|
||||
// Step 14. If response is not a network error and response is not a filtered response, then:
|
||||
let mut response = if !response.is_network_error() && response.internal_response.is_none() {
|
||||
// Substep 1.
|
||||
if request.response_tainting == ResponseTainting::CorsTainting {
|
||||
@@ -675,6 +675,9 @@ pub async fn main_fetch(
|
||||
internal_response.url_list.clone_from(&request.url_list)
|
||||
}
|
||||
|
||||
// Step 17. Set internalResponse’s redirect taint to request’s redirect-taint.
|
||||
internal_response.redirect_taint = request.redirect_taint_for_request();
|
||||
|
||||
// Step 19. If response is not a network error and any of the following returns blocked
|
||||
// * should internalResponse to request be blocked as mixed content
|
||||
// * should internalResponse to request be blocked by Content Security Policy
|
||||
@@ -745,7 +748,7 @@ pub async fn main_fetch(
|
||||
response
|
||||
};
|
||||
|
||||
// Step 19.
|
||||
// Step 19. If response is not a network error and any of the following returns blocked
|
||||
let mut response_loaded = false;
|
||||
let mut response = if !response.is_network_error() && !request.integrity_metadata.is_empty() {
|
||||
// Step 19.1.
|
||||
|
||||
@@ -55,7 +55,9 @@ use net_traits::request::{
|
||||
is_cors_non_wildcard_request_header_name, is_cors_safelisted_method,
|
||||
is_cors_safelisted_request_header,
|
||||
};
|
||||
use net_traits::response::{CacheState, HttpsState, Response, ResponseBody, ResponseType};
|
||||
use net_traits::response::{
|
||||
CacheState, HttpsState, RedirectTaint, Response, ResponseBody, ResponseType,
|
||||
};
|
||||
use net_traits::{
|
||||
CookieSource, DOCUMENT_ACCEPT_HEADER_VALUE, DebugVec, FetchMetadata, NetworkError,
|
||||
RedirectEndValue, RedirectStartValue, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
|
||||
@@ -2558,9 +2560,8 @@ fn cors_check(request: &Request, response: &Response) -> Result<(), ()> {
|
||||
}
|
||||
|
||||
// Step 4. If the result of byte-serializing a request origin with request is not origin, then return failure.
|
||||
match request.origin {
|
||||
Origin::Origin(ref o) if *o.ascii_serialization() == *origin => {},
|
||||
_ => return Err(()),
|
||||
if serialize_request_origin(request).to_string() != origin {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Step 5. If request’s credentials mode is not "include", then return success.
|
||||
@@ -2606,38 +2607,6 @@ fn is_redirect_status(status: StatusCode) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-tainted-origin>
|
||||
fn request_has_redirect_tainted_origin(request: &Request) -> bool {
|
||||
// Step 1. Assert: request’s origin is not "client".
|
||||
let Origin::Origin(request_origin) = &request.origin else {
|
||||
panic!("origin cannot be \"client\" at this point in time");
|
||||
};
|
||||
|
||||
// Step 2. Let lastURL be null.
|
||||
let mut last_url = None;
|
||||
|
||||
// Step 3. For each url of request’s URL list:
|
||||
for url in &request.url_list {
|
||||
// Step 3.1 If lastURL is null, then set lastURL to url and continue.
|
||||
let Some(last_url) = &mut last_url else {
|
||||
last_url = Some(url);
|
||||
continue;
|
||||
};
|
||||
|
||||
// Step 3.2 If url’s origin is not same origin with lastURL’s origin and
|
||||
// request’s origin is not same origin with lastURL’s origin, then return true.
|
||||
if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Step 3.3 Set lastURL to url.
|
||||
*last_url = url;
|
||||
}
|
||||
|
||||
// Step 4. Return false.
|
||||
false
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#serializing-a-request-origin>
|
||||
fn serialize_request_origin(request: &Request) -> headers::Origin {
|
||||
// Step 1. Assert: request’s origin is not "client".
|
||||
@@ -2645,8 +2614,8 @@ fn serialize_request_origin(request: &Request) -> headers::Origin {
|
||||
panic!("origin cannot be \"client\" at this point in time");
|
||||
};
|
||||
|
||||
// Step 2. If request has a redirect-tainted origin, then return "null".
|
||||
if request_has_redirect_tainted_origin(request) {
|
||||
// Step 2. If request’s redirect-taint is not "same-origin", then return "null".
|
||||
if request.redirect_taint_for_request() != RedirectTaint::SameOrigin {
|
||||
return headers::Origin::NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ use url::Position;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::policy_container::{PolicyContainer, RequestPolicyContainer};
|
||||
use crate::response::{HttpsState, Response};
|
||||
use crate::pub_domains::is_same_site;
|
||||
use crate::response::{HttpsState, RedirectTaint, Response};
|
||||
use crate::{ReferrerPolicy, ResourceTimingType};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||
@@ -1001,6 +1002,49 @@ impl Request {
|
||||
// Step 5. Return totalRequestLength.
|
||||
total_request_length
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#concept-request-tainted-origin>
|
||||
pub fn redirect_taint_for_request(&self) -> RedirectTaint {
|
||||
// Step 1. Assert: request’s origin is not "client".
|
||||
let Origin::Origin(request_origin) = &self.origin else {
|
||||
unreachable!("origin cannot be \"client\" at this point in time");
|
||||
};
|
||||
|
||||
// Step 2. Let lastURL be null.
|
||||
let mut last_url = None;
|
||||
|
||||
// Step 3. Let taint be "same-origin".
|
||||
let mut taint = RedirectTaint::SameOrigin;
|
||||
|
||||
// Step 4. For each url of request’s URL list:
|
||||
for url in &self.url_list {
|
||||
// Step 4.1 If lastURL is null, then set lastURL to url and continue.
|
||||
let Some(last_url) = &mut last_url else {
|
||||
last_url = Some(url);
|
||||
continue;
|
||||
};
|
||||
|
||||
// Step 4.2. If url’s origin is not same site with lastURL’s origin and
|
||||
// request’s origin is not same site with lastURL’s origin, then return "cross-site".
|
||||
if !is_same_site(&url.origin(), &last_url.origin()) &&
|
||||
!is_same_site(request_origin, &last_url.origin())
|
||||
{
|
||||
return RedirectTaint::CrossSite;
|
||||
}
|
||||
|
||||
// Step 4.3. If url’s origin is not same origin with lastURL’s origin
|
||||
// and request’s origin is not same origin with lastURL’s origin, then set taint to "same-site".
|
||||
if url.origin() != last_url.origin() && *request_origin != last_url.origin() {
|
||||
taint = RedirectTaint::SameSite;
|
||||
}
|
||||
|
||||
// Step 4.4 Set lastURL to url.
|
||||
*last_url = url;
|
||||
}
|
||||
|
||||
// Step 5. Return taint.
|
||||
taint
|
||||
}
|
||||
}
|
||||
|
||||
impl Referrer {
|
||||
|
||||
@@ -58,6 +58,15 @@ impl ResponseBody {
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://fetch.spec.whatwg.org/#response-redirect-taint>
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
|
||||
pub enum RedirectTaint {
|
||||
#[default]
|
||||
SameOrigin,
|
||||
SameSite,
|
||||
CrossSite,
|
||||
}
|
||||
|
||||
/// [Cache state](https://fetch.spec.whatwg.org/#concept-response-cache-state)
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub enum CacheState {
|
||||
@@ -109,6 +118,8 @@ pub struct Response {
|
||||
pub https_state: HttpsState,
|
||||
pub tls_security_info: Option<TlsSecurityInfo>,
|
||||
pub referrer: Option<ServoUrl>,
|
||||
/// <https://fetch.spec.whatwg.org/#response-redirect-taint>
|
||||
pub redirect_taint: RedirectTaint,
|
||||
pub referrer_policy: ReferrerPolicy,
|
||||
/// [CORS-exposed header-name list](https://fetch.spec.whatwg.org/#concept-response-cors-exposed-header-name-list)
|
||||
pub cors_exposed_header_name_list: Vec<String>,
|
||||
@@ -152,6 +163,7 @@ impl Response {
|
||||
aborted: Arc::new(AtomicBool::new(false)),
|
||||
resource_timing: Arc::new(Mutex::new(resource_timing)),
|
||||
range_requested: false,
|
||||
redirect_taint: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +199,7 @@ impl Response {
|
||||
ResourceTimingType::Error,
|
||||
))),
|
||||
range_requested: false,
|
||||
redirect_taint: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user