Implement PolicyContainer and update the default ReferrerPolicy (#33977)

* Implement PolicyContainer

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

* implement small parts of fetch that interact with policy container

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

* fix: allow policy container's csp list to be unset

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

* fix: use the correct default policy when parsing from a token

Signed-off-by: Shane Handley <shanehandley@fastmail.com>

---------

Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
shanehandley 2024-11-08 18:19:23 +11:00 committed by GitHub
parent 4f6283d7fe
commit 6451767428
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
201 changed files with 210 additions and 5178 deletions

View file

@ -20,6 +20,7 @@ use log::warn;
use mime::{self, Mime};
use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
use net_traits::http_status::HttpStatus;
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
use net_traits::request::{
is_cors_safelisted_method, is_cors_safelisted_request_header, BodyChunkRequest,
BodyChunkResponse, CredentialsMode, Destination, Origin, RedirectMode, Referrer, Request,
@ -27,8 +28,8 @@ use net_traits::request::{
};
use net_traits::response::{Response, ResponseBody, ResponseType};
use net_traits::{
FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceAttribute, ResourceFetchTiming,
ResourceTimeValue, ResourceTimingType,
FetchTaskTarget, NetworkError, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue,
ResourceTimingType,
};
use rustls::Certificate;
use serde::{Deserialize, Serialize};
@ -114,52 +115,83 @@ pub async fn fetch(request: &mut Request, target: Target<'_>, context: &FetchCon
fetch_with_cors_cache(request, &mut CorsCache::default(), target, context).await;
}
/// Continuation of fetch from step 9.
///
/// <https://fetch.spec.whatwg.org#concept-fetch>
pub async fn fetch_with_cors_cache(
request: &mut Request,
cache: &mut CorsCache,
target: Target<'_>,
context: &FetchContext,
) {
// Step 1.
// Step 9: If requests window is "client", then set requests window to requests client, if
// requests clients global object is a Window object; otherwise "no-window".
if request.window == Window::Client {
// TODO: Set window to request's client object if client is a Window object
} else {
request.window = Window::NoWindow;
}
// Step 2.
// Step 10: If requests origin is "client", then set requests origin to requests clients
// origin.
if request.origin == Origin::Client {
// TODO: set request's origin to request's client's origin
unimplemented!()
}
// Step 3.
set_default_accept(request);
// Step 11: If all of the following conditions are true:
// - requests URLs scheme is an HTTP(S) scheme
// - requests mode is "same-origin", "cors", or "no-cors"
// - requests window is an environment settings object
// - requests method is `GET`
// - requests unsafe-request flag is not set or requests header list is empty
// TODO: evaluate these conditions when we have an an environment settings object
// Step 4.
set_default_accept_language(&mut request.headers);
// Step 12: If requests policy container is "client", then:
if let RequestPolicyContainer::Client = request.policy_container {
// Step 12.1: If requests client is non-null, then set requests policy container to a clone
// of requests clients policy container.
// TODO: Requires request's client to support PolicyContainer
// Step 5.
// TODO: figure out what a Priority object is.
// Step 6.
// TODO: handle client hints headers.
// Step 7.
if request.is_subresource_request() {
// TODO: handle client hints headers.
// Step 12.2: Otherwise, set requests policy container to a new policy container.
request.policy_container =
RequestPolicyContainer::PolicyContainer(PolicyContainer::default());
}
// Step 8.
// Step 13: If requests header list does not contain `Accept`:
set_default_accept(request);
// Step 14: If requests header list does not contain `Accept-Language`, then user agents should
// append (`Accept-Language, an appropriate header value) to requests header list.
set_default_accept_language(&mut request.headers);
// Step 15. If requests internal priority is null, then use requests priority, initiator,
// destination, and render-blocking in an implementation-defined manner to set requests
// internal priority to an implementation-defined object.
// TODO: figure out what a Priority object is.
// Step 16: If request is a subresource request, then:
if request.is_subresource_request() {
// TODO: requires keepalive.
}
// Step 17: Run main fetch given fetchParams.
main_fetch(request, cache, false, false, target, &mut None, context).await;
// Step 18: Return fetchParamss controller.
// TODO: We don't implement fetchParams as defined in the spec
}
/// <https://www.w3.org/TR/CSP/#should-block-request>
pub fn should_request_be_blocked_by_csp(request: &Request) -> csp::CheckResult {
pub fn should_request_be_blocked_by_csp(
request: &Request,
policy_container: &PolicyContainer,
) -> csp::CheckResult {
let origin = match &request.origin {
Origin::Client => return csp::CheckResult::Allowed,
Origin::Origin(origin) => origin,
};
let csp_request = csp::Request {
url: request.url().into_url(),
origin: origin.clone().into_url_origin(),
@ -170,8 +202,9 @@ pub fn should_request_be_blocked_by_csp(request: &Request) -> csp::CheckResult {
integrity_metadata: request.integrity_metadata.clone(),
parser_metadata: csp::ParserMetadata::None,
};
// TODO: Instead of ignoring violations, report them.
request
policy_container
.csp_list
.as_ref()
.map(|c| c.should_request_be_blocked(&csp_request).0)
@ -213,8 +246,15 @@ 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 {
RequestPolicyContainer::Client => PolicyContainer::default(),
RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
};
// Step 2.4.
if should_request_be_blocked_by_csp(request) == csp::CheckResult::Blocked {
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(
"Blocked by Content-Security-Policy".into(),
@ -236,16 +276,14 @@ pub async fn main_fetch(
// TODO: handle blocking as mixed content.
// TODO: handle blocking by content security policy.
// Step 6
// TODO: handle request's client's referrer policy.
// Step 7.
// Step 8: If requests referrer policy is the empty string, then set requests referrer policy
// to requests policy containers referrer policy.
request.referrer_policy = request
.referrer_policy
.or(Some(ReferrerPolicy::NoReferrerWhenDowngrade));
.or(Some(policy_container.referrer_policy));
// Step 8.
assert!(request.referrer_policy.is_some());
let referrer_url = match mem::replace(&mut request.referrer, Referrer::NoReferrer) {
Referrer::NoReferrer => None,
Referrer::ReferrerUrl(referrer_source) | Referrer::Client(referrer_source) => {