Set correct policy-container for worker construction (#36603)

This makes sure that when workers are created, their global scope has
the correct policy-container set
so that we can do CSP-checks.

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-04-21 14:47:06 +02:00 committed by GitHub
parent d724c8e9e3
commit 9a14ad8535
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 67 additions and 136 deletions

View file

@ -479,6 +479,7 @@ impl DedicatedWorkerGlobalScope {
Ok((metadata, bytes)) => (metadata, bytes),
};
scope.set_url(metadata.final_url);
scope.set_csp_list(GlobalScope::parse_csp_list_from_metadata(&metadata.headers));
global_scope.set_https_state(metadata.https_state);
let source = String::from_utf8_lossy(&bytes);

View file

@ -22,7 +22,7 @@ use constellation_traits::{
ScriptToConstellationChan, ScriptToConstellationMessage,
};
use content_security_policy::{
CheckResult, CspList, PolicyDisposition, Violation, ViolationResource,
CheckResult, CspList, PolicyDisposition, PolicySource, Violation, ViolationResource,
};
use crossbeam_channel::Sender;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
@ -30,6 +30,8 @@ use dom_struct::dom_struct;
use embedder_traits::{
EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType,
};
use http::HeaderMap;
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::{IsWrapper, UnwrapObjectDynamic};
@ -2415,6 +2417,27 @@ impl GlobalScope {
unreachable!();
}
/// <https://www.w3.org/TR/CSP/#initialize-document-csp>
pub(crate) fn parse_csp_list_from_metadata(
headers: &Option<Serde<HeaderMap>>,
) -> Option<CspList> {
// TODO: Implement step 1 (local scheme special case)
let mut csp = headers.as_ref()?.get_all("content-security-policy").iter();
// This silently ignores the CSP if it contains invalid Unicode.
// We should probably report an error somewhere.
let c = csp.next().and_then(|c| c.to_str().ok())?;
let mut csp_list = CspList::parse(c, PolicySource::Header, PolicyDisposition::Enforce);
for c in csp {
let c = c.to_str().ok()?;
csp_list.append(CspList::parse(
c,
PolicySource::Header,
PolicyDisposition::Enforce,
));
}
Some(csp_list)
}
/// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url)
/// for this global scope.
pub(crate) fn api_base_url(&self) -> ServoUrl {
@ -3089,10 +3112,10 @@ impl GlobalScope {
/// <https://www.w3.org/TR/CSP/#get-csp-of-object>
pub(crate) fn get_csp_list(&self) -> Option<CspList> {
if self.downcast::<Window>().is_some() {
if self.downcast::<Window>().is_some() || self.downcast::<WorkerGlobalScope>().is_some() {
return self.policy_container().csp_list;
}
// TODO: Worker and Worklet global scopes.
// TODO: Worklet global scopes.
None
}

View file

@ -9,7 +9,7 @@ use base::cross_process_instant::CrossProcessInstant;
use base::id::PipelineId;
use base64::Engine as _;
use base64::engine::general_purpose;
use content_security_policy::{self as csp, CspList};
use content_security_policy as csp;
use dom_struct::dom_struct;
use embedder_traits::resources::{self, Resource};
use encoding_rs::Encoding;
@ -59,6 +59,7 @@ use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLD
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::documenttype::DocumentType;
use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlinputelement::HTMLInputElement;
@ -850,29 +851,9 @@ impl FetchResponseListener for ParserContext {
.map(Serde::into_inner)
.map(Into::into);
// https://www.w3.org/TR/CSP/#initialize-document-csp
// TODO: Implement step 1 (local scheme special case)
let csp_list = metadata.as_ref().and_then(|m| {
let h = m.headers.as_ref()?;
let mut csp = h.get_all("content-security-policy").iter();
// This silently ignores the CSP if it contains invalid Unicode.
// We should probably report an error somewhere.
let c = csp.next().and_then(|c| c.to_str().ok())?;
let mut csp_list = CspList::parse(
c,
csp::PolicySource::Header,
csp::PolicyDisposition::Enforce,
);
for c in csp {
let c = c.to_str().ok()?;
csp_list.append(CspList::parse(
c,
csp::PolicySource::Header,
csp::PolicyDisposition::Enforce,
));
}
Some(csp_list)
});
let csp_list = metadata
.as_ref()
.and_then(|m| GlobalScope::parse_csp_list_from_metadata(&m.headers));
let parser = match ScriptThread::page_headers_available(&self.id, metadata, CanGc::note()) {
Some(parser) => parser,

View file

@ -12,6 +12,7 @@ use std::time::Duration;
use base::cross_process_instant::CrossProcessInstant;
use base::id::{PipelineId, PipelineNamespace};
use constellation_traits::WorkerGlobalScopeInit;
use content_security_policy::CspList;
use crossbeam_channel::Receiver;
use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
use dom_struct::dom_struct;
@ -246,6 +247,10 @@ impl WorkerGlobalScope {
self.policy_container.borrow()
}
pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
self.policy_container.borrow_mut().set_csp_list(csp_list);
}
/// Get a mutable reference to the [`TimerScheduler`] for this [`ServiceWorkerGlobalScope`].
pub(crate) fn timer_scheduler(&self) -> RefMut<TimerScheduler> {
self.timer_scheduler.borrow_mut()
@ -300,6 +305,7 @@ impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
.use_url_credentials(true)
.origin(global_scope.origin().immutable().clone())
.insecure_requests_policy(self.insecure_requests_policy())
.policy_container(global_scope.policy_container())
.has_trustworthy_ancestor_origin(
global_scope.has_trustworthy_ancestor_or_current_origin(),
)