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;