From fc20d8b2e19df81cfddb29c2cbe036e4615ebda6 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Tue, 24 Jun 2025 10:50:30 +0200 Subject: [PATCH] Move CSP code into one entrypoint (#37604) This refactoring moves various CSP-related methods away from GlobalScope and Document into a dedicated entrypoint. It also reduces the amount of imports of the CSP crate, so that types are consolidated into this one entrypoint. That way, we control how CSP code interacts with the script crate. For reviewing purposes, I split up the refactoring into separate distinct commits that all move 1 method(group) into the new file. Testing: no change in behavior, only a build improvement + code cleanup --------- Signed-off-by: Tim van der Lippe Signed-off-by: Tim van der Lippe --- components/script/dom/csp.rs | 304 ++++++++++++++++++ .../script/dom/dedicatedworkerglobalscope.rs | 3 +- components/script/dom/document.rs | 27 +- components/script/dom/element.rs | 16 +- components/script/dom/eventsource.rs | 4 +- components/script/dom/eventtarget.rs | 16 +- components/script/dom/globalscope.rs | 167 +--------- components/script/dom/htmliframeelement.rs | 11 +- components/script/dom/htmlimageelement.rs | 4 +- components/script/dom/htmllinkelement.rs | 6 +- components/script/dom/htmlmediaelement.rs | 4 +- components/script/dom/htmlscriptelement.rs | 19 +- components/script/dom/htmlstyleelement.rs | 16 +- components/script/dom/htmlvideoelement.rs | 4 +- components/script/dom/mod.rs | 1 + components/script/dom/notification.rs | 7 +- components/script/dom/servoparser/mod.rs | 7 +- components/script/dom/servoparser/prefetch.rs | 3 +- .../script/dom/trustedtypepolicyfactory.rs | 33 +- components/script/dom/websocket.rs | 2 +- components/script/dom/xmlhttprequest.rs | 4 +- components/script/fetch.rs | 4 +- components/script/layout_image.rs | 4 +- components/script/script_module.rs | 4 +- components/script/script_runtime.rs | 19 +- components/script/script_thread.rs | 7 +- components/script/security_manager.rs | 7 +- components/script/stylesheet_loader.rs | 4 +- components/script/timers.rs | 6 +- 29 files changed, 424 insertions(+), 289 deletions(-) create mode 100644 components/script/dom/csp.rs diff --git a/components/script/dom/csp.rs b/components/script/dom/csp.rs new file mode 100644 index 00000000000..ddef9498ee4 --- /dev/null +++ b/components/script/dom/csp.rs @@ -0,0 +1,304 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::borrow::Cow; + +use constellation_traits::{LoadData, LoadOrigin}; +/// Used to determine which inline check to run +pub use content_security_policy::InlineCheckType; +/// Used to report CSP violations in Fetch handlers +pub use content_security_policy::Violation; +use content_security_policy::{ + CheckResult, CspList, Destination, Element as CspElement, Initiator, NavigationCheckType, + Origin, ParserMetadata, PolicyDisposition, PolicySource, Request, ViolationResource, +}; +use http::HeaderMap; +use hyper_serde::Serde; +use js::rust::describe_scripted_caller; + +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::csppolicyviolationreport::CSPViolationReportBuilder; +use crate::dom::element::Element; +use crate::dom::globalscope::GlobalScope; +use crate::dom::node::{Node, NodeTraits}; +use crate::dom::window::Window; +use crate::security_manager::CSPViolationReportTask; + +pub(crate) trait CspReporting { + fn is_js_evaluation_allowed(&self, global: &GlobalScope, source: &str) -> bool; + fn is_wasm_evaluation_allowed(&self, global: &GlobalScope) -> bool; + fn should_navigation_request_be_blocked( + &self, + global: &GlobalScope, + load_data: &LoadData, + element: Option<&Element>, + ) -> bool; + fn should_elements_inline_type_behavior_be_blocked( + &self, + global: &GlobalScope, + el: &Element, + type_: InlineCheckType, + source: &str, + ) -> bool; + fn is_trusted_type_policy_creation_allowed( + &self, + global: &GlobalScope, + policy_name: String, + created_policy_names: Vec, + ) -> bool; + fn does_sink_type_require_trusted_types( + &self, + sink_group: &str, + include_report_only_policies: bool, + ) -> bool; + fn should_sink_type_mismatch_violation_be_blocked_by_csp( + &self, + global: &GlobalScope, + sink: &str, + sink_group: &str, + source: &str, + ) -> bool; +} + +impl CspReporting for Option { + /// + fn is_js_evaluation_allowed(&self, global: &GlobalScope, source: &str) -> bool { + let Some(csp_list) = self else { + return true; + }; + + let (is_js_evaluation_allowed, violations) = csp_list.is_js_evaluation_allowed(source); + + global.report_csp_violations(violations, None); + + is_js_evaluation_allowed == CheckResult::Allowed + } + + /// + fn is_wasm_evaluation_allowed(&self, global: &GlobalScope) -> bool { + let Some(csp_list) = self else { + return true; + }; + + let (is_wasm_evaluation_allowed, violations) = csp_list.is_wasm_evaluation_allowed(); + + global.report_csp_violations(violations, None); + + is_wasm_evaluation_allowed == CheckResult::Allowed + } + + /// + fn should_navigation_request_be_blocked( + &self, + global: &GlobalScope, + load_data: &LoadData, + element: Option<&Element>, + ) -> bool { + let Some(csp_list) = self else { + return false; + }; + let request = Request { + url: load_data.url.clone().into_url(), + origin: match &load_data.load_origin { + LoadOrigin::Script(immutable_origin) => immutable_origin.clone().into_url_origin(), + _ => Origin::new_opaque(), + }, + // TODO: populate this field correctly + redirect_count: 0, + destination: Destination::None, + initiator: Initiator::None, + nonce: "".to_owned(), + integrity_metadata: "".to_owned(), + parser_metadata: ParserMetadata::None, + }; + // TODO: set correct navigation check type for form submission if applicable + let (result, violations) = + csp_list.should_navigation_request_be_blocked(&request, NavigationCheckType::Other); + + global.report_csp_violations(violations, element); + + result == CheckResult::Blocked + } + + /// + fn should_elements_inline_type_behavior_be_blocked( + &self, + global: &GlobalScope, + el: &Element, + type_: InlineCheckType, + source: &str, + ) -> bool { + let Some(csp_list) = self else { + return false; + }; + let element = CspElement { + nonce: el.nonce_value_if_nonceable().map(Cow::Owned), + }; + let (result, violations) = + csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source); + + global.report_csp_violations(violations, Some(el)); + + result == CheckResult::Blocked + } + + /// + fn is_trusted_type_policy_creation_allowed( + &self, + global: &GlobalScope, + policy_name: String, + created_policy_names: Vec, + ) -> bool { + let Some(csp_list) = self else { + return true; + }; + + let (allowed_by_csp, violations) = + csp_list.is_trusted_type_policy_creation_allowed(policy_name, created_policy_names); + + global.report_csp_violations(violations, None); + + allowed_by_csp == CheckResult::Allowed + } + + /// + fn does_sink_type_require_trusted_types( + &self, + sink_group: &str, + include_report_only_policies: bool, + ) -> bool { + let Some(csp_list) = self else { + return false; + }; + + csp_list.does_sink_type_require_trusted_types(sink_group, include_report_only_policies) + } + + /// + fn should_sink_type_mismatch_violation_be_blocked_by_csp( + &self, + global: &GlobalScope, + sink: &str, + sink_group: &str, + source: &str, + ) -> bool { + let Some(csp_list) = self else { + return false; + }; + + let (allowed_by_csp, violations) = csp_list + .should_sink_type_mismatch_violation_be_blocked_by_csp(sink, sink_group, source); + + global.report_csp_violations(violations, None); + + allowed_by_csp == CheckResult::Blocked + } +} + +pub(crate) trait GlobalCspReporting { + fn report_csp_violations(&self, violations: Vec, element: Option<&Element>); +} + +impl GlobalCspReporting for GlobalScope { + /// + #[allow(unsafe_code)] + fn report_csp_violations(&self, violations: Vec, element: Option<&Element>) { + let scripted_caller = + unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default(); + for violation in violations { + let (sample, resource) = match violation.resource { + ViolationResource::Inline { sample } => (sample, "inline".to_owned()), + ViolationResource::Url(url) => (None, url.into()), + ViolationResource::TrustedTypePolicy { sample } => { + (Some(sample), "trusted-types-policy".to_owned()) + }, + ViolationResource::TrustedTypeSink { sample } => { + (Some(sample), "trusted-types-sink".to_owned()) + }, + ViolationResource::Eval { sample } => (sample, "eval".to_owned()), + ViolationResource::WasmEval => (None, "wasm-eval".to_owned()), + }; + let report = CSPViolationReportBuilder::default() + .resource(resource) + .sample(sample) + .effective_directive(violation.directive.name) + .original_policy(violation.policy.to_string()) + .report_only(violation.policy.disposition == PolicyDisposition::Report) + .source_file(scripted_caller.filename.clone()) + .line_number(scripted_caller.line) + .column_number(scripted_caller.col + 1) + .build(self); + // Step 1: Let global be violation’s global object. + // We use `self` as `global`; + // Step 2: Let target be violation’s element. + let target = element.and_then(|event_target| { + // Step 3.1: If target is not null, and global is a Window, + // and target’s shadow-including root is not global’s associated Document, set target to null. + if let Some(window) = self.downcast::() { + // If a node is connected, its owner document is always the shadow-including root. + // If it isn't connected, then it also doesn't have a corresponding document, hence + // it can't be this document. + if event_target.upcast::().owner_document() != window.Document() { + return None; + } + } + Some(event_target) + }); + let target = match target { + // Step 3.2: If target is null: + None => { + // Step 3.2.2: If target is a Window, set target to target’s associated Document. + if let Some(window) = self.downcast::() { + Trusted::new(window.Document().upcast()) + } else { + // Step 3.2.1: Set target to violation’s global object. + Trusted::new(self.upcast()) + } + }, + Some(event_target) => Trusted::new(event_target.upcast()), + }; + // Step 3: Queue a task to run the following steps: + let task = + CSPViolationReportTask::new(Trusted::new(self), target, report, violation.policy); + self.task_manager() + .dom_manipulation_task_source() + .queue(task); + } + } +} + +/// +pub(crate) fn parse_csp_list_from_metadata(headers: &Option>) -> Option { + // TODO: Implement step 1 (local scheme special case) + let headers = headers.as_ref()?; + let mut csp = headers.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, + )); + } + let csp_report = headers + .get_all("content-security-policy-report-only") + .iter(); + // This silently ignores the CSP if it contains invalid Unicode. + // We should probably report an error somewhere. + for c in csp_report { + let c = c.to_str().ok()?; + csp_list.append(CspList::parse( + c, + PolicySource::Header, + PolicyDisposition::Report, + )); + } + Some(csp_list) +} diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 1e87cf02687..b29c8001a1c 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -43,6 +43,7 @@ use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox}; use crate::dom::bindings::utils::define_all_exposed_interfaces; +use crate::dom::csp::parse_csp_list_from_metadata; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; @@ -479,7 +480,7 @@ impl DedicatedWorkerGlobalScope { Ok((metadata, bytes)) => (metadata, bytes), }; scope.set_url(metadata.final_url.clone()); - scope.set_csp_list(GlobalScope::parse_csp_list_from_metadata(&metadata.headers)); + scope.set_csp_list(parse_csp_list_from_metadata(&metadata.headers)); global_scope.set_https_state(metadata.https_state); let source = String::from_utf8_lossy(&bytes); if let Some(chan) = global_scope.devtools_chan() { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 0e7992799da..22b3bc24e89 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -22,7 +21,7 @@ use canvas_traits::canvas::CanvasId; use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg}; use chrono::Local; use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage}; -use content_security_policy::{self as csp, CspList, PolicyDisposition}; +use content_security_policy::{CspList, PolicyDisposition}; use cookie::Cookie; use cssparser::match_ignore_ascii_case; use data_url::mime::Mime; @@ -4306,30 +4305,6 @@ impl Document { self.policy_container.borrow().csp_list.clone() } - /// - pub(crate) fn should_elements_inline_type_behavior_be_blocked( - &self, - el: &Element, - type_: csp::InlineCheckType, - source: &str, - ) -> csp::CheckResult { - let (result, violations) = match self.get_csp_list() { - None => { - return csp::CheckResult::Allowed; - }, - Some(csp_list) => { - let element = csp::Element { - nonce: el.nonce_value_if_nonceable().map(Cow::Owned), - }; - csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source) - }, - }; - - self.global().report_csp_violations(violations, Some(el)); - - result - } - /// Prevent any JS or layout from running until the corresponding call to /// `remove_script_and_layout_blocker`. Used to isolate periods in which /// the DOM is in an unstable state and should not be exposed to arbitrary diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 58129470860..618ac732664 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -12,7 +12,6 @@ use std::rc::Rc; use std::str::FromStr; use std::{fmt, mem}; -use content_security_policy as csp; use cssparser::{Parser as CssParser, ParserInput as CssParserInput, match_ignore_ascii_case}; use devtools_traits::AttrInfo; use dom_struct::dom_struct; @@ -99,6 +98,7 @@ use crate::dom::bindings::xmlname::{ }; use crate::dom::characterdata::CharacterData; use crate::dom::create::create_element; +use crate::dom::csp::{CspReporting, InlineCheckType}; use crate::dom::customelementregistry::{ CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState, is_valid_custom_element_name, @@ -2259,15 +2259,19 @@ impl Element { } else { let win = self.owner_window(); let source = &**attr.value(); + let global = &self.owner_global(); // However, if the Should element's inline behavior be blocked by // Content Security Policy? algorithm returns "Blocked" when executed // upon the attribute's element, "style attribute", and the attribute's value, // then the style rules defined in the attribute's value must not be applied to the element. [CSP] - if doc.should_elements_inline_type_behavior_be_blocked( - self, - csp::InlineCheckType::StyleAttribute, - source, - ) == csp::CheckResult::Blocked + if global + .get_csp_list() + .should_elements_inline_type_behavior_be_blocked( + global, + self, + InlineCheckType::StyleAttribute, + source, + ) { return; } diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 3e0070f91d7..465b80c17b7 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -8,7 +8,6 @@ use std::str::{Chars, FromStr}; use std::sync::{Arc, Mutex}; use std::time::Duration; -use content_security_policy as csp; use dom_struct::dom_struct; use headers::ContentType; use http::StatusCode; @@ -37,6 +36,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -474,7 +474,7 @@ impl FetchResponseListener for EventSourceContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 15f77f5fcd5..7bdf949394b 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -11,7 +11,6 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; -use content_security_policy as csp; use deny_public_fields::DenyPublicFields; use dom_struct::dom_struct; use fnv::FnvHasher; @@ -56,6 +55,7 @@ use crate::dom::bindings::reflector::{ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::HashMapTracedValues; +use crate::dom::csp::{CspReporting, InlineCheckType}; use crate::dom::document::Document; use crate::dom::element::Element; use crate::dom::errorevent::ErrorEvent; @@ -556,11 +556,15 @@ impl EventTarget { ) { if let Some(element) = self.downcast::() { let doc = element.owner_document(); - if doc.should_elements_inline_type_behavior_be_blocked( - element.upcast(), - csp::InlineCheckType::ScriptAttribute, - source, - ) == csp::CheckResult::Blocked + let global = &doc.global(); + if global + .get_csp_list() + .should_elements_inline_type_behavior_be_blocked( + global, + element.upcast(), + InlineCheckType::ScriptAttribute, + source, + ) { return; } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index f3b6e8acd6e..0fe101a0d99 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -18,19 +18,14 @@ use base::id::{ ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId, }; use constellation_traits::{ - BlobData, BlobImpl, BroadcastMsg, FileBlob, LoadData, LoadOrigin, MessagePortImpl, - MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage, -}; -use content_security_policy::{ - CheckResult, CspList, Destination, Initiator, NavigationCheckType, ParserMetadata, - PolicyDisposition, PolicySource, Request, Violation, ViolationResource, + BlobData, BlobImpl, BroadcastMsg, FileBlob, MessagePortImpl, MessagePortMsg, PortMessageTask, + ScriptToConstellationChan, ScriptToConstellationMessage, }; +use content_security_policy::CspList; use crossbeam_channel::Sender; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; use embedder_traits::EmbedderMsg; -use http::HeaderMap; -use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::glue::{IsWrapper, UnwrapObjectDynamic}; @@ -43,8 +38,7 @@ use js::panic::maybe_resume_unwind; use js::rust::wrappers::{JS_ExecuteScript, JS_GetScriptPrivate}; use js::rust::{ CompileOptionsWrapper, CustomAutoRooter, CustomAutoRooterGuard, HandleValue, - MutableHandleValue, ParentRuntime, Runtime, describe_scripted_caller, get_object_class, - transform_str_to_source_text, + MutableHandleValue, ParentRuntime, Runtime, get_object_class, transform_str_to_source_text, }; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; use net_traits::blob_url_store::{BlobBuf, get_blob_origin}; @@ -63,7 +57,6 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim use script_bindings::interfaces::GlobalScopeHelpers; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use timers::{TimerEventRequest, TimerId}; -use url::Origin; use uuid::Uuid; #[cfg(feature = "webgpu")] use webgpu_traits::{DeviceLostReason, WebGPUDevice}; @@ -101,11 +94,9 @@ use crate::dom::bindings::weakref::{DOMTracker, WeakRef}; use crate::dom::blob::Blob; use crate::dom::broadcastchannel::BroadcastChannel; use crate::dom::crypto::Crypto; -use crate::dom::csppolicyviolationreport::CSPViolationReportBuilder; use crate::dom::dedicatedworkerglobalscope::{ DedicatedWorkerControlMsg, DedicatedWorkerGlobalScope, }; -use crate::dom::element::Element; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventsource::EventSource; @@ -113,7 +104,6 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::file::File; use crate::dom::htmlscriptelement::{ScriptId, SourceCode}; use crate::dom::messageport::MessagePort; -use crate::dom::node::{Node, NodeTraits}; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::performance::Performance; use crate::dom::performanceobserver::VALID_ENTRY_TYPES; @@ -141,7 +131,6 @@ use crate::script_module::{ }; use crate::script_runtime::{CanGc, JSContext as SafeJSContext, ThreadSafeJSContext}; use crate::script_thread::{ScriptThread, with_script_thread}; -use crate::security_manager::CSPViolationReportTask; use crate::task_manager::TaskManager; use crate::task_source::SendableTaskSource; use crate::timers::{ @@ -2520,41 +2509,6 @@ impl GlobalScope { unreachable!(); } - /// - pub(crate) fn parse_csp_list_from_metadata( - headers: &Option>, - ) -> Option { - // TODO: Implement step 1 (local scheme special case) - let headers = headers.as_ref()?; - let mut csp = headers.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, - )); - } - let csp_report = headers - .get_all("content-security-policy-report-only") - .iter(); - // This silently ignores the CSP if it contains invalid Unicode. - // We should probably report an error somewhere. - for c in csp_report { - let c = c.to_str().ok()?; - csp_list.append(CspList::parse( - c, - PolicySource::Header, - PolicyDisposition::Report, - )); - } - 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 { @@ -2939,49 +2893,6 @@ impl GlobalScope { })) } - pub(crate) fn is_js_evaluation_allowed(&self, source: &str) -> bool { - let Some(csp_list) = self.get_csp_list() else { - return true; - }; - - let (is_js_evaluation_allowed, violations) = csp_list.is_js_evaluation_allowed(source); - - self.report_csp_violations(violations, None); - - is_js_evaluation_allowed == CheckResult::Allowed - } - - pub(crate) fn should_navigation_request_be_blocked( - &self, - load_data: &LoadData, - element: Option<&Element>, - ) -> bool { - let Some(csp_list) = self.get_csp_list() else { - return false; - }; - let request = Request { - url: load_data.url.clone().into_url(), - origin: match &load_data.load_origin { - LoadOrigin::Script(immutable_origin) => immutable_origin.clone().into_url_origin(), - _ => Origin::new_opaque(), - }, - // TODO: populate this field correctly - redirect_count: 0, - destination: Destination::None, - initiator: Initiator::None, - nonce: "".to_owned(), - integrity_metadata: "".to_owned(), - parser_metadata: ParserMetadata::None, - }; - // TODO: set correct navigation check type for form submission if applicable - let (result, violations) = - csp_list.should_navigation_request_be_blocked(&request, NavigationCheckType::Other); - - self.report_csp_violations(violations, element); - - result == CheckResult::Blocked - } - pub(crate) fn fire_timer(&self, handle: TimerEventId, can_gc: CanGc) { self.timers().fire_timer(handle, self, can_gc); } @@ -3421,76 +3332,6 @@ impl GlobalScope { unreachable!(); } - /// - #[allow(unsafe_code)] - pub(crate) fn report_csp_violations( - &self, - violations: Vec, - element: Option<&Element>, - ) { - let scripted_caller = - unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default(); - for violation in violations { - let (sample, resource) = match violation.resource { - ViolationResource::Inline { sample } => (sample, "inline".to_owned()), - ViolationResource::Url(url) => (None, url.into()), - ViolationResource::TrustedTypePolicy { sample } => { - (Some(sample), "trusted-types-policy".to_owned()) - }, - ViolationResource::TrustedTypeSink { sample } => { - (Some(sample), "trusted-types-sink".to_owned()) - }, - ViolationResource::Eval { sample } => (sample, "eval".to_owned()), - ViolationResource::WasmEval => (None, "wasm-eval".to_owned()), - }; - let report = CSPViolationReportBuilder::default() - .resource(resource) - .sample(sample) - .effective_directive(violation.directive.name) - .original_policy(violation.policy.to_string()) - .report_only(violation.policy.disposition == PolicyDisposition::Report) - .source_file(scripted_caller.filename.clone()) - .line_number(scripted_caller.line) - .column_number(scripted_caller.col + 1) - .build(self); - // Step 1: Let global be violation’s global object. - // We use `self` as `global`; - // Step 2: Let target be violation’s element. - let target = element.and_then(|event_target| { - // Step 3.1: If target is not null, and global is a Window, - // and target’s shadow-including root is not global’s associated Document, set target to null. - if let Some(window) = self.downcast::() { - // If a node is connected, its owner document is always the shadow-including root. - // If it isn't connected, then it also doesn't have a corresponding document, hence - // it can't be this document. - if event_target.upcast::().owner_document() != window.Document() { - return None; - } - } - Some(event_target) - }); - let target = match target { - // Step 3.2: If target is null: - None => { - // Step 3.2.2: If target is a Window, set target to target’s associated Document. - if let Some(window) = self.downcast::() { - Trusted::new(window.Document().upcast()) - } else { - // Step 3.2.1: Set target to violation’s global object. - Trusted::new(self.upcast()) - } - }, - Some(event_target) => Trusted::new(event_target.upcast()), - }; - // Step 3: Queue a task to run the following steps: - let task = - CSPViolationReportTask::new(Trusted::new(self), target, report, violation.policy); - self.task_manager() - .dom_manipulation_task_source() - .queue(task); - } - } - pub(crate) fn import_map(&self) -> Ref<'_, ImportMap> { self.import_map.borrow() } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index a0c837fc020..be2641a0fee 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -34,6 +34,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::csp::CspReporting; use crate::dom::document::{Document, determine_policy_for_token}; use crate::dom::domtokenlist::DOMTokenList; use crate::dom::element::{ @@ -166,10 +167,12 @@ impl HTMLIFrameElement { if load_data.url.scheme() == "javascript" { let window_proxy = self.GetContentWindow(); if let Some(window_proxy) = window_proxy { - if document - .global() - .should_navigation_request_be_blocked(&load_data, Some(self.upcast())) - { + let global = &document.global(); + if global.get_csp_list().should_navigation_request_be_blocked( + global, + &load_data, + Some(self.upcast()), + ) { return; } // Important re security. See https://github.com/servo/servo/issues/23373 diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index d872b793533..f8d39c5fa8b 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -10,7 +10,6 @@ use std::sync::Arc; use std::{char, mem}; use app_units::{AU_PER_PX, Au}; -use content_security_policy as csp; use cssparser::{Parser, ParserInput}; use dom_struct::dom_struct; use euclid::Point2D; @@ -60,6 +59,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::{Document, determine_policy_for_token}; use crate::dom::element::{ AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers, @@ -295,7 +295,7 @@ impl FetchResponseListener for ImageContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 41edb2c22d3..fa22372460d 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -8,7 +8,6 @@ use std::default::Default; use std::str::FromStr; use base::id::WebViewId; -use content_security_policy as csp; use dom_struct::dom_struct; use embedder_traits::EmbedderMsg; use html5ever::{LocalName, Prefix, local_name, ns}; @@ -39,6 +38,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::cssstylesheet::CSSStyleSheet; use crate::dom::document::Document; use crate::dom::domtokenlist::DOMTokenList; @@ -1016,7 +1016,7 @@ impl FetchResponseListener for PrefetchContext { submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } @@ -1090,7 +1090,7 @@ impl FetchResponseListener for PreloadContext { submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 484493f3003..68ac43905f4 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -10,7 +10,6 @@ use std::time::{Duration, Instant}; use std::{f64, mem}; use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData}; -use content_security_policy as csp; use dom_struct::dom_struct; use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState}; use euclid::default::Size2D; @@ -74,6 +73,7 @@ use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::blob::Blob; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::Document; use crate::dom::element::{ AttributeMutation, Element, ElementCreator, cors_setting_for_element, @@ -3167,7 +3167,7 @@ impl FetchResponseListener for HTMLMediaElementFetchListener { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 325000e773b..9a1dc3535e8 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -12,7 +12,6 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use base::id::{PipelineId, WebViewId}; -use content_security_policy as csp; use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo}; use dom_struct::dom_struct; use encoding_rs::Encoding; @@ -58,6 +57,7 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::settings_stack::AutoEntryScript; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::trace::NoTrace; +use crate::dom::csp::{CspReporting, GlobalCspReporting, InlineCheckType, Violation}; use crate::dom::document::Document; use crate::dom::element::{ AttributeMutation, Element, ElementCreator, cors_setting_for_element, @@ -557,7 +557,7 @@ impl FetchResponseListener for ClassicContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); let elem = self.elem.root(); global.report_csp_violations(violations, Some(elem.upcast::())); @@ -763,13 +763,18 @@ impl HTMLScriptElement { return; } + let global = &doc.global(); + // Step 19. CSP. if !element.has_attribute(&local_name!("src")) && - doc.should_elements_inline_type_behavior_be_blocked( - element, - csp::InlineCheckType::Script, - &text, - ) == csp::CheckResult::Blocked + global + .get_csp_list() + .should_elements_inline_type_behavior_be_blocked( + global, + element, + InlineCheckType::Script, + &text, + ) { warn!("Blocking inline script due to CSP"); return; diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index aed08b7bcf6..0852e889675 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -4,7 +4,6 @@ use std::cell::Cell; -use content_security_policy as csp; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use js::rust::HandleObject; @@ -20,6 +19,7 @@ use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; +use crate::dom::csp::{CspReporting, InlineCheckType}; use crate::dom::cssstylesheet::CSSStyleSheet; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element, ElementCreator}; @@ -99,15 +99,19 @@ impl HTMLStyleElement { } let doc = self.owner_document(); + let global = &self.owner_global(); // Step 5: If the Should element's inline behavior be blocked by Content Security Policy? algorithm // returns "Blocked" when executed upon the style element, "style", // and the style element's child text content, then return. [CSP] - if doc.should_elements_inline_type_behavior_be_blocked( - self.upcast(), - csp::InlineCheckType::Style, - &node.child_text_content(), - ) == csp::CheckResult::Blocked + if global + .get_csp_list() + .should_elements_inline_type_behavior_be_blocked( + global, + self.upcast(), + InlineCheckType::Style, + &node.child_text_content(), + ) { return; } diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index b59960606a3..52d62a841f6 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -5,7 +5,6 @@ use std::cell::Cell; use std::sync::Arc; -use content_security_policy as csp; use dom_struct::dom_struct; use euclid::default::Size2D; use html5ever::{LocalName, Prefix, local_name, ns}; @@ -34,6 +33,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, LayoutDom}; use crate::dom::bindings::str::DOMString; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers}; use crate::dom::globalscope::GlobalScope; @@ -463,7 +463,7 @@ impl FetchResponseListener for PosterFrameFetchContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 663c399c9d0..f82a9790946 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -263,6 +263,7 @@ pub(crate) mod countqueuingstrategy; mod create; pub(crate) mod crypto; pub(crate) mod cryptokey; +pub(crate) mod csp; pub(crate) mod csppolicyviolationreport; pub(crate) mod css; pub(crate) mod cssconditionrule; diff --git a/components/script/dom/notification.rs b/components/script/dom/notification.rs index 10550a4f219..f60f74d3a53 100644 --- a/components/script/dom/notification.rs +++ b/components/script/dom/notification.rs @@ -7,8 +7,6 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{SystemTime, UNIX_EPOCH}; -use content_security_policy as csp; -use content_security_policy::Destination; use dom_struct::dom_struct; use embedder_traits::{ EmbedderMsg, Notification as EmbedderNotification, @@ -24,7 +22,7 @@ use net_traits::image_cache::{ ImageCache, ImageCacheResponseMessage, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable, ImageResponse, PendingImageId, UsePlaceholder, }; -use net_traits::request::{RequestBuilder, RequestId}; +use net_traits::request::{Destination, RequestBuilder, RequestId}; use net_traits::{ FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ResourceFetchTiming, ResourceTimingType, @@ -55,6 +53,7 @@ use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::bindings::utils::to_frozen_array; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::permissions::{PermissionAlgorithm, Permissions, descriptor_permission_state}; @@ -793,7 +792,7 @@ impl FetchResponseListener for ResourceFetchListener { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index a0164035a7a..981ec22dc7f 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -9,7 +9,6 @@ use base::cross_process_instant::CrossProcessInstant; use base::id::PipelineId; use base64::Engine as _; use base64::engine::general_purpose; -use content_security_policy as csp; use devtools_traits::ScriptToDevtoolsControlMsg; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource}; @@ -57,11 +56,11 @@ use crate::dom::bindings::settings_stack::is_execution_stack_empty; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::characterdata::CharacterData; use crate::dom::comment::Comment; +use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata}; use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument}; 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; @@ -908,7 +907,7 @@ impl FetchResponseListener for ParserContext { let csp_list = metadata .as_ref() - .and_then(|m| GlobalScope::parse_csp_list_from_metadata(&m.headers)); + .and_then(|m| parse_csp_list_from_metadata(&m.headers)); let parser = match ScriptThread::page_headers_available(&self.id, metadata, CanGc::note()) { Some(parser) => parser, @@ -1105,7 +1104,7 @@ impl FetchResponseListener for ParserContext { ); } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let parser = match self.parser.as_ref() { Some(parser) => parser.root(), None => return, diff --git a/components/script/dom/servoparser/prefetch.rs b/components/script/dom/servoparser/prefetch.rs index f0faec8df89..f56ef114b34 100644 --- a/components/script/dom/servoparser/prefetch.rs +++ b/components/script/dom/servoparser/prefetch.rs @@ -6,7 +6,6 @@ use std::cell::{Cell, RefCell}; use std::ops::Deref; use base::id::{PipelineId, WebViewId}; -use content_security_policy::Destination; use html5ever::buffer_queue::BufferQueue; use html5ever::tokenizer::states::RawKind; use html5ever::tokenizer::{ @@ -17,7 +16,7 @@ use js::jsapi::JSTracer; use markup5ever::TokenizerResult; use net_traits::policy_container::PolicyContainer; use net_traits::request::{ - CorsSettings, CredentialsMode, InsecureRequestsPolicy, ParserMetadata, Referrer, + CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, Referrer, }; use net_traits::{CoreResourceMsg, FetchChannels, IpcSend, ReferrerPolicy, ResourceThreads}; use servo_url::{ImmutableOrigin, ServoUrl}; diff --git a/components/script/dom/trustedtypepolicyfactory.rs b/components/script/dom/trustedtypepolicyfactory.rs index 5c971daf811..8b385efcc8d 100644 --- a/components/script/dom/trustedtypepolicyfactory.rs +++ b/components/script/dom/trustedtypepolicyfactory.rs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::RefCell; -use content_security_policy::CheckResult; use dom_struct::dom_struct; use html5ever::{LocalName, Namespace, QualName, local_name, ns}; use js::jsval::NullValue; @@ -17,6 +16,7 @@ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; +use crate::dom::csp::CspReporting; use crate::dom::globalscope::GlobalScope; use crate::dom::trustedhtml::TrustedHTML; use crate::dom::trustedscript::TrustedScript; @@ -57,19 +57,16 @@ impl TrustedTypePolicyFactory { ) -> Fallible> { // Step 1: Let allowedByCSP be the result of executing Should Trusted Type policy creation be blocked by // Content Security Policy? algorithm with global, policyName and factory’s created policy names value. - let (allowed_by_csp, violations) = if let Some(csp_list) = global.get_csp_list() { - csp_list.is_trusted_type_policy_creation_allowed( + let allowed_by_csp = global + .get_csp_list() + .is_trusted_type_policy_creation_allowed( + global, policy_name.clone(), self.policy_names.borrow().clone(), - ) - } else { - (CheckResult::Allowed, Vec::new()) - }; - - global.report_csp_violations(violations, None); + ); // Step 2: If allowedByCSP is "Blocked", throw a TypeError and abort further steps. - if allowed_by_csp == CheckResult::Blocked { + if !allowed_by_csp { return Err(Error::Type("Not allowed by CSP".to_string())); } @@ -198,13 +195,11 @@ impl TrustedTypePolicyFactory { sink_group: &str, can_gc: CanGc, ) -> Fallible { - let csp_list = match global.get_csp_list() { - None => return Ok(input), - Some(csp_list) => csp_list, - }; // Step 2: Let requireTrustedTypes be the result of executing Does sink type require trusted types? // algorithm, passing global, sinkGroup, and true. - let require_trusted_types = csp_list.does_sink_type_require_trusted_types(sink_group, true); + let require_trusted_types = global + .get_csp_list() + .does_sink_type_require_trusted_types(sink_group, true); // Step 3: If requireTrustedTypes is false, return stringified input and abort these steps. if !require_trusted_types { return Ok(input); @@ -225,13 +220,13 @@ impl TrustedTypePolicyFactory { // Step 6.1: Let disposition be the result of executing Should sink type mismatch violation // be blocked by Content Security Policy? algorithm, passing global, // stringified input as source, sinkGroup and sink. - let (disposition, violations) = csp_list + let is_blocked = global + .get_csp_list() .should_sink_type_mismatch_violation_be_blocked_by_csp( - sink, sink_group, &input, + global, sink, sink_group, &input, ); - global.report_csp_violations(violations, None); // Step 6.2: If disposition is “Allowed”, return stringified input and abort further steps. - if disposition == CheckResult::Allowed { + if !is_blocked { Ok(input) } else { // Step 6.3: Throw a TypeError and abort further steps. diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index bbb637dfe28..ee5569fd6d7 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -7,7 +7,6 @@ use std::cell::Cell; use std::ptr; use constellation_traits::BlobImpl; -use content_security_policy::Violation; use dom_struct::dom_struct; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; @@ -38,6 +37,7 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::{DOMString, USVString, is_token}; use crate::dom::blob::Blob; use crate::dom::closeevent::CloseEvent; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 858cd24d45d..c52967f4717 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -11,7 +11,6 @@ use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; use constellation_traits::BlobImpl; -use content_security_policy as csp; use data_url::mime::Mime; use dom_struct::dom_struct; use encoding_rs::{Encoding, UTF_8}; @@ -56,6 +55,7 @@ use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::{ByteString, DOMString, USVString, is_token}; use crate::dom::blob::{Blob, normalize_type_string}; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument}; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; @@ -147,7 +147,7 @@ impl FetchResponseListener for XHRContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 989cdba862a..f3a69d3021c 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -6,7 +6,6 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use base::id::WebViewId; -use content_security_policy as csp; use ipc_channel::ipc; use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer}; use net_traits::request::{ @@ -31,6 +30,7 @@ use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::globalscope::GlobalScope; use crate::dom::headers::Guard; use crate::dom::performanceresourcetiming::InitiatorType; @@ -311,7 +311,7 @@ impl FetchResponseListener for FetchContext { } } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs index 546e758e38c..b33c9f890df 100644 --- a/components/script/layout_image.rs +++ b/components/script/layout_image.rs @@ -8,7 +8,6 @@ use std::sync::Arc; -use content_security_policy as csp; use net_traits::image_cache::{ImageCache, PendingImageId}; use net_traits::request::{Destination, RequestBuilder as FetchRequestInit, RequestId}; use net_traits::{ @@ -20,6 +19,7 @@ use servo_url::ServoUrl; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::DomRoot; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::Document; use crate::dom::globalscope::GlobalScope; use crate::dom::node::{Node, NodeTraits}; @@ -79,7 +79,7 @@ impl FetchResponseListener for LayoutImageContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/script_module.rs b/components/script/script_module.rs index f292a62e071..2de40a8084c 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -11,7 +11,6 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::{mem, ptr}; -use content_security_policy as csp; use encoding_rs::UTF_8; use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; use html5ever::local_name; @@ -61,6 +60,7 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::settings_stack::AutoIncumbentScript; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::Document; use crate::dom::dynamicmoduleowner::{DynamicModuleId, DynamicModuleOwner}; use crate::dom::element::Element; @@ -1367,7 +1367,7 @@ impl FetchResponseListener for ModuleContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index f26eb586b94..d0db73b6792 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -19,7 +19,6 @@ use std::time::{Duration, Instant}; use std::{os, ptr, thread}; use background_hang_monitor_api::ScriptHangAnnotation; -use content_security_policy::CheckResult; use js::conversions::jsstr_to_string; use js::glue::{ CollectServoSizes, CreateJobQueue, DeleteJobQueue, DispatchableRun, JobQueueTraps, @@ -72,6 +71,7 @@ use crate::dom::bindings::reflector::{DomGlobal, DomObject}; use crate::dom::bindings::root::trace_roots; use crate::dom::bindings::utils::DOM_CALLBACKS; use crate::dom::bindings::{principals, settings_stack}; +use crate::dom::csp::CspReporting; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -380,25 +380,20 @@ unsafe extern "C" fn content_security_policy_allows( wrap_panic(&mut || { // SpiderMonkey provides null pointer when executing webassembly. let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); - let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); - let Some(csp_list) = global.get_csp_list() else { - allowed = true; - return; - }; + let global = &GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); - let (is_evaluation_allowed, violations) = match runtime_code { + allowed = match runtime_code { RuntimeCode::JS => { let source = match sample { sample if !sample.is_null() => &jsstr_to_string(*cx, *sample), _ => "", }; - csp_list.is_js_evaluation_allowed(source) + global + .get_csp_list() + .is_js_evaluation_allowed(global, source) }, - RuntimeCode::WASM => csp_list.is_wasm_evaluation_allowed(), + RuntimeCode::WASM => global.get_csp_list().is_wasm_evaluation_allowed(global), }; - - global.report_csp_violations(violations, None); - allowed = is_evaluation_allowed == CheckResult::Allowed; }); allowed } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 748be207296..1a02b8a4ef1 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -41,7 +41,6 @@ use constellation_traits::{ JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan, ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType, }; -use content_security_policy::{self as csp}; use crossbeam_channel::unbounded; use data_url::mime::Mime; use devtools_traits::{ @@ -119,6 +118,7 @@ use crate::dom::bindings::root::{ use crate::dom::bindings::settings_stack::AutoEntryScript; use crate::dom::bindings::str::DOMString; use crate::dom::bindings::trace::{HashMapTracedValues, JSTraceable}; +use crate::dom::csp::{CspReporting, GlobalCspReporting, Violation}; use crate::dom::customelementregistry::{ CallbackReaction, CustomElementDefinition, CustomElementReactionStack, }; @@ -622,9 +622,10 @@ impl ScriptThread { let task = task!(navigate_javascript: move || { // Important re security. See https://github.com/servo/servo/issues/23373 if let Some(window) = trusted_global.root().downcast::() { + let global = &trusted_global.root(); // Step 5: If the result of should navigation request of type be blocked by // Content Security Policy? given request and cspNavigationType is "Blocked", then return. [CSP] - if trusted_global.root().should_navigation_request_be_blocked(&load_data, None) { + if global.get_csp_list().should_navigation_request_be_blocked(global, &load_data, None) { return; } if ScriptThread::check_load_origin(&load_data.load_origin, &window.get_url().origin()) { @@ -3845,7 +3846,7 @@ impl ScriptThread { } } - fn handle_csp_violations(&self, id: PipelineId, _: RequestId, violations: Vec) { + fn handle_csp_violations(&self, id: PipelineId, _: RequestId, violations: Vec) { if let Some(global) = self.documents.borrow().find_global(id) { // TODO(https://github.com/w3c/webappsec-csp/issues/687): Update after spec is resolved global.report_csp_violations(violations, None); diff --git a/components/script/security_manager.rs b/components/script/security_manager.rs index fb7e78a8862..52e54349bea 100644 --- a/components/script/security_manager.rs +++ b/components/script/security_manager.rs @@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex}; use content_security_policy as csp; use headers::{ContentType, HeaderMap, HeaderMapExt}; use net_traits::request::{ - CredentialsMode, RequestBody, RequestId, create_request_body_with_content, + CredentialsMode, Destination, RequestBody, RequestId, create_request_body_with_content, }; use net_traits::{ FetchMetadata, FetchResponseListener, NetworkError, ResourceFetchTiming, ResourceTimingType, @@ -19,6 +19,7 @@ use crate::conversions::Convert; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::root::DomRoot; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::csppolicyviolationreport::{ CSPReportUriViolationReport, SecurityPolicyViolationReport, }; @@ -110,7 +111,7 @@ impl CSPViolationReportTask { let request = create_a_potential_cors_request( None, endpoint.clone(), - csp::Destination::Report, + Destination::Report, None, None, global.get_referrer(), @@ -204,7 +205,7 @@ impl FetchResponseListener for CSPReportUriFetchListener { submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 5efaf78e542..f73677401e4 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -5,7 +5,6 @@ use std::io::{Read, Seek, Write}; use std::sync::atomic::AtomicBool; -use content_security_policy as csp; use cssparser::SourceLocation; use encoding_rs::UTF_8; use mime::{self, Mime}; @@ -32,6 +31,7 @@ use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::DomRoot; +use crate::dom::csp::{GlobalCspReporting, Violation}; use crate::dom::document::Document; use crate::dom::element::Element; use crate::dom::eventtarget::EventTarget; @@ -296,7 +296,7 @@ impl FetchResponseListener for StylesheetContext { network_listener::submit_timing(self, CanGc::note()) } - fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { + fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec) { let global = &self.resource_timing_global(); global.report_csp_violations(violations, None); } diff --git a/components/script/timers.rs b/components/script/timers.rs index d41ca7f39cb..19e004afa7d 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -26,6 +26,7 @@ use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::{DomGlobal, DomObject}; use crate::dom::bindings::root::Dom; use crate::dom::bindings::str::DOMString; +use crate::dom::csp::CspReporting; use crate::dom::document::{ImageAnimationUpdateCallback, RefreshRedirectDue}; use crate::dom::eventsource::EventSourceTimeoutCallback; use crate::dom::globalscope::GlobalScope; @@ -426,7 +427,10 @@ impl JsTimers { ) -> i32 { let callback = match callback { TimerCallback::StringTimerCallback(code_str) => { - if global.is_js_evaluation_allowed(code_str.as_ref()) { + if global + .get_csp_list() + .is_js_evaluation_allowed(global, code_str.as_ref()) + { InternalTimerCallback::StringTimerCallback(code_str) } else { return 0;