diff options
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/document.rs | 40 | ||||
-rw-r--r-- | components/script/dom/globalscope.rs | 10 | ||||
-rw-r--r-- | components/script/dom/htmlscriptelement.rs | 12 | ||||
-rw-r--r-- | components/script/dom/request.rs | 4 | ||||
-rw-r--r-- | components/script/dom/servoparser/mod.rs | 28 |
6 files changed, 93 insertions, 4 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index e86c96e0a9e..a914b8161a6 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -54,6 +54,7 @@ use canvas_traits::webgl::{ use canvas_traits::webgl::{WebGLFramebufferId, WebGLMsgSender, WebGLPipeline, WebGLProgramId}; use canvas_traits::webgl::{WebGLReceiver, WebGLRenderbufferId, WebGLSLVersion, WebGLSender}; use canvas_traits::webgl::{WebGLShaderId, WebGLSyncId, WebGLTextureId, WebGLVersion}; +use content_security_policy::CspList; use crossbeam_channel::{Receiver, Sender}; use cssparser::RGBA; use devtools_traits::{CSSError, TimelineMarkerType, WorkerId}; @@ -170,6 +171,8 @@ unsafe_no_jsmanaged_fields!(*mut JobQueue); unsafe_no_jsmanaged_fields!(Cow<'static, str>); +unsafe_no_jsmanaged_fields!(CspList); + /// Trace a `JSVal`. pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: &Heap<JSVal>) { unsafe { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 452212a2b3c..6581dbd54bb 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -110,6 +110,7 @@ use crate::task::TaskBox; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::OneshotTimerCallback; use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg}; +use content_security_policy::{self as csp, CspList}; use cookie::Cookie; use devtools_traits::ScriptToDevtoolsControlMsg; use dom_struct::dom_struct; @@ -137,6 +138,7 @@ use num_traits::ToPrimitive; use percent_encoding::percent_decode; use profile_traits::ipc as profile_ipc; use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType}; +use ref_filter_map::ref_filter_map; use ref_slice::ref_slice; use script_layout_interface::message::{Msg, ReflowGoal}; use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType}; @@ -148,7 +150,7 @@ use servo_atoms::Atom; use servo_config::pref; use servo_media::{ClientContextId, ServoMedia}; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; -use std::borrow::ToOwned; +use std::borrow::Cow; use std::cell::{Cell, Ref, RefMut}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet, VecDeque}; @@ -398,6 +400,9 @@ pub struct Document { media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>, /// List of all WebGL context IDs that need flushing. dirty_webgl_contexts: DomRefCell<HashSet<WebGLContextId>>, + /// https://html.spec.whatwg.org/multipage/#concept-document-csp-list + #[ignore_malloc_size_of = "Defined in rust-content-security-policy"] + csp_list: DomRefCell<Option<CspList>>, } #[derive(JSTraceable, MallocSizeOf)] @@ -1734,9 +1739,10 @@ impl Document { pub fn fetch_async( &self, load: LoadType, - request: RequestBuilder, + mut request: RequestBuilder, fetch_target: IpcSender<FetchResponseMsg>, ) { + request.csp_list = self.get_csp_list().map(|x| x.clone()); let mut loader = self.loader.borrow_mut(); loader.fetch_async(load, request, fetch_target); } @@ -2806,9 +2812,39 @@ impl Document { shadow_roots_styles_changed: Cell::new(false), media_controls: DomRefCell::new(HashMap::new()), dirty_webgl_contexts: DomRefCell::new(HashSet::new()), + csp_list: DomRefCell::new(None), } } + pub fn set_csp_list(&self, csp_list: Option<CspList>) { + *self.csp_list.borrow_mut() = csp_list; + } + + pub fn get_csp_list(&self) -> Option<Ref<CspList>> { + ref_filter_map(self.csp_list.borrow(), Option::as_ref) + } + + /// https://www.w3.org/TR/CSP/#should-block-inline + pub fn should_elements_inline_type_behavior_be_blocked( + &self, + el: &Element, + type_: csp::InlineCheckType, + source: &str, + ) -> csp::CheckResult { + let element = csp::Element { + nonce: el + .get_attribute(&ns!(), &local_name!("nonce")) + .map(|attr| Cow::Owned(attr.value().to_string())), + }; + // TODO: Instead of ignoring violations, report them. + self.get_csp_list() + .map(|c| { + c.should_elements_inline_type_behavior_be_blocked(&element, type_, source) + .0 + }) + .unwrap_or(csp::CheckResult::Allowed) + } + /// 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/globalscope.rs b/components/script/dom/globalscope.rs index 2ae306e25be..c7635e98463 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -38,6 +38,7 @@ use crate::task_source::websocket::WebsocketTaskSource; use crate::task_source::TaskSourceName; use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle}; use crate::timers::{OneshotTimers, TimerCallback}; +use content_security_policy::CspList; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; @@ -812,6 +813,15 @@ impl GlobalScope { pub fn get_user_agent(&self) -> Cow<'static, str> { self.user_agent.clone() } + + /// https://www.w3.org/TR/CSP/#get-csp-of-object + pub fn get_csp_list(&self) -> Option<CspList> { + if let Some(window) = self.downcast::<Window>() { + return window.Document().get_csp_list().map(|c| c.clone()); + } + // TODO: Worker and Worklet global scopes. + None + } } fn timestamp_in_ms(time: Timespec) -> u64 { diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 1bd0101c03d..dfac55a6f66 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -27,6 +27,7 @@ use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::create_a_potential_CORS_request; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; +use content_security_policy as csp; use dom_struct::dom_struct; use encoding_rs::Encoding; use html5ever::{LocalName, Prefix}; @@ -428,7 +429,16 @@ impl HTMLScriptElement { // TODO: Step 12: nomodule content attribute - // TODO(#4577): Step 13: CSP. + // Step 13. + if !element.has_attribute(&local_name!("src")) && + doc.should_elements_inline_type_behavior_be_blocked( + &element, + csp::InlineCheckType::Script, + &text, + ) == csp::CheckResult::Blocked + { + return; + } // Step 14. let for_attribute = element.get_attribute(&ns!(), &local_name!("for")); diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index 5393288c633..d22d4680355 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -755,7 +755,9 @@ impl Into<RequestDestination> for NetTraitsRequestDestination { NetTraitsRequestDestination::Object => RequestDestination::Object, NetTraitsRequestDestination::Report => RequestDestination::Report, NetTraitsRequestDestination::Script => RequestDestination::Script, - NetTraitsRequestDestination::ServiceWorker => { + NetTraitsRequestDestination::ServiceWorker | + NetTraitsRequestDestination::AudioWorklet | + NetTraitsRequestDestination::PaintWorklet => { panic!("ServiceWorker request destination should not be exposed to DOM") }, NetTraitsRequestDestination::SharedWorker => RequestDestination::Sharedworker, diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index a05dea2a66d..91b5c9bfce6 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -35,6 +35,7 @@ use crate::dom::text::Text; use crate::dom::virtualmethods::vtable_for; use crate::network_listener::PreInvoke; use crate::script_thread::ScriptThread; +use content_security_policy::{self as csp, CspList}; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource}; use encoding_rs::Encoding; @@ -736,6 +737,31 @@ impl FetchResponseListener for ParserContext { .and_then(|meta| meta.content_type) .map(Serde::into_inner) .map(Into::into); + + // https://www.w3.org/TR/CSP/#initialize-document-csp + // TODO: Implement step 1 (local scheme special case) + let csp_list = metadata.as_ref().and_then(|m| { + let h = m.headers.as_ref()?; + let mut csp = h.get_all("content-security-policy").iter(); + // This silently ignores the CSP if it contains invalid Unicode. + // We should probably report an error somewhere. + let c = csp.next().and_then(|c| c.to_str().ok())?; + let mut csp_list = CspList::parse( + c, + csp::PolicySource::Header, + csp::PolicyDisposition::Enforce, + ); + for c in csp { + let c = c.to_str().ok()?; + csp_list.append(CspList::parse( + c, + csp::PolicySource::Header, + csp::PolicyDisposition::Enforce, + )); + } + Some(csp_list) + }); + let parser = match ScriptThread::page_headers_available(&self.id, metadata) { Some(parser) => parser, None => return, @@ -744,6 +770,8 @@ impl FetchResponseListener for ParserContext { return; } + parser.document.set_csp_list(csp_list); + self.parser = Some(Trusted::new(&*parser)); match content_type { |