diff options
author | Gregory Terzian <gterzian@users.noreply.github.com> | 2019-05-12 17:37:19 +0800 |
---|---|---|
committer | Gregory Terzian <gterzian@users.noreply.github.com> | 2019-07-18 12:03:45 +0800 |
commit | 571beec179fe9fd5fff2c12b3c5dfa0a5d93df01 (patch) | |
tree | 2eda42b78fa99fd2cd51d733519d5ae9d8678a66 /components/script/script_thread.rs | |
parent | 973a3448a459464b79ea0ef5fb46141176cc7643 (diff) | |
download | servo-571beec179fe9fd5fff2c12b3c5dfa0a5d93df01.tar.gz servo-571beec179fe9fd5fff2c12b3c5dfa0a5d93df01.zip |
clean-up navigation
security: check target and source origin before executing JS url
implement replacement-enabled flag as a HistoryEntryReplacement enum
add source origin string on loaddata
add LoadOrigin
iframe: remove optional load-data
auxiliaries: add load-data into info
constellation: remove url from Pipeline::new
check load origin: link to whatwg issue
switch loadorigin toplevel to constellation
Diffstat (limited to 'components/script/script_thread.rs')
-rw-r--r-- | components/script/script_thread.rs | 146 |
1 files changed, 95 insertions, 51 deletions
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d0dad1cd9ba..2265bc8a379 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -32,6 +32,7 @@ use crate::dom::bindings::conversions::{ }; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; +use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::ThreadLocalStackRoots; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection}; @@ -82,6 +83,7 @@ use crate::task_source::performance_timeline::PerformanceTimelineTaskSource; use crate::task_source::remote_event::RemoteEventTaskSource; use crate::task_source::user_interaction::UserInteractionTaskSource; use crate::task_source::websocket::WebsocketTaskSource; +use crate::task_source::TaskSource; use crate::task_source::TaskSourceName; use crate::webdriver_handlers; use bluetooth_traits::BluetoothRequest; @@ -128,8 +130,10 @@ use script_traits::CompositorEvent::{ WheelEvent, }; use script_traits::{CompositorEvent, ConstellationControlMsg}; -use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult}; -use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData}; +use script_traits::{ + DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, +}; +use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData, LoadOrigin}; use script_traits::{MouseButton, MouseEventType, NewLayoutInfo}; use script_traits::{Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory}; use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg}; @@ -263,10 +267,6 @@ enum MixedMessage { pub enum MainThreadScriptMsg { /// Common variants associated with the script messages Common(CommonScriptMsg), - /// Begins a content-initiated load on the specified pipeline (only - /// dispatched to ScriptThread). Allows for a replace bool to be passed. If true, - /// the current entry will be replaced instead of a new entry being added. - Navigate(PipelineId, LoadData, bool), /// Notifies the script thread that a new worklet has been loaded, and thus the page should be /// reflowed. WorkletLoaded(PipelineId), @@ -855,6 +855,74 @@ impl ScriptThread { }); } + /// Check that two origins are "similar enough", + /// for now only used to prevent cross-origin JS url evaluation. + /// + /// https://github.com/whatwg/html/issues/2591 + pub fn check_load_origin(source: &LoadOrigin, target: &ImmutableOrigin) -> bool { + match (source, target) { + (LoadOrigin::Constellation, _) | (LoadOrigin::WebDriver, _) => { + // Always allow loads initiated by the constellation or webdriver. + true + }, + (_, ImmutableOrigin::Opaque(_)) => { + // If the target is opaque, allow. + // This covers newly created about:blank auxiliaries, and iframe with no src. + // TODO: https://github.com/servo/servo/issues/22879 + true + }, + (LoadOrigin::Script(source_origin), _) => source_origin == target, + } + } + + /// Step 13 of https://html.spec.whatwg.org/multipage/#navigate + pub fn navigate( + pipeline_id: PipelineId, + mut load_data: LoadData, + replace: HistoryEntryReplacement, + ) { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = match root.get() { + None => return, + Some(script) => script, + }; + let script_thread = unsafe { &*script_thread }; + let is_javascript = load_data.url.scheme() == "javascript"; + // If resource is a request whose url's scheme is "javascript" + // https://html.spec.whatwg.org/multipage/#javascript-protocol + if is_javascript { + let window = match script_thread.documents.borrow().find_window(pipeline_id) { + None => return, + Some(window) => window, + }; + let global = window.upcast::<GlobalScope>(); + let trusted_global = Trusted::new(global); + let sender = script_thread.script_sender.clone(); + let task = task!(navigate_javascript: move || { + // Important re security. See https://github.com/servo/servo/issues/23373 + // TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request + if let Some(window) = trusted_global.root().downcast::<Window>() { + if ScriptThread::check_load_origin(&load_data.load_origin, &window.get_url().origin()) { + ScriptThread::eval_js_url(&trusted_global.root(), &mut load_data); + sender + .send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) + .unwrap(); + } + } + }); + global + .dom_manipulation_task_source() + .queue(task, global.upcast()) + .expect("Enqueing navigate js task on the DOM manipulation task source failed"); + } else { + script_thread + .script_sender + .send((pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) + .expect("Sending a LoadUrl message to the constellation failed"); + } + }); + } + pub fn process_attach_layout(new_layout_info: NewLayoutInfo, origin: MutableOrigin) { SCRIPT_THREAD_ROOT.with(|root| { if let Some(script_thread) = root.get() { @@ -1474,7 +1542,7 @@ impl ScriptThread { SetDocumentActivity(id, ..) => Some(id), ChangeFrameVisibilityStatus(id, ..) => Some(id), NotifyVisibilityChange(id, ..) => Some(id), - Navigate(id, ..) => Some(id), + NavigateIframe(id, ..) => Some(id), PostMessage { target: id, .. } => Some(id), UpdatePipelineId(_, _, _, id, _) => Some(id), UpdateHistoryState(id, ..) => Some(id), @@ -1504,7 +1572,6 @@ impl ScriptThread { pipeline_id }, MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(_)) => None, - MainThreadScriptMsg::Navigate(pipeline_id, ..) => Some(pipeline_id), MainThreadScriptMsg::WorkletLoaded(pipeline_id) => Some(pipeline_id), MainThreadScriptMsg::RegisterPaintWorklet { pipeline_id, .. } => Some(pipeline_id), MainThreadScriptMsg::DispatchJobQueue { .. } => None, @@ -1614,14 +1681,14 @@ impl ScriptThread { _ => unreachable!(), }; }, - ConstellationControlMsg::Navigate( + ConstellationControlMsg::NavigateIframe( parent_pipeline_id, browsing_context_id, load_data, replace, - ) => self.handle_navigate( + ) => self.handle_navigate_iframe( parent_pipeline_id, - Some(browsing_context_id), + browsing_context_id, load_data, replace, ), @@ -1736,9 +1803,6 @@ impl ScriptThread { fn handle_msg_from_script(&self, msg: MainThreadScriptMsg) { match msg { - MainThreadScriptMsg::Navigate(parent_pipeline_id, load_data, replace) => { - self.handle_navigate(parent_pipeline_id, None, load_data, replace) - }, MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task, _, _)) => task.run_box(), MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => { self.collect_reports(chan) @@ -3248,50 +3312,30 @@ impl ScriptThread { document.handle_wheel_event(self.js_runtime.rt(), wheel_delta, point, node_address); } - /// <https://html.spec.whatwg.org/multipage/#navigating-across-documents> - /// The entry point for content to notify that a new load has been requested - /// for the given pipeline (specifically the "navigate" algorithm). - fn handle_navigate( + /// Handle a "navigate an iframe" message from the constellation. + fn handle_navigate_iframe( &self, parent_pipeline_id: PipelineId, - browsing_context_id: Option<BrowsingContextId>, - mut load_data: LoadData, - replace: bool, + browsing_context_id: BrowsingContextId, + load_data: LoadData, + replace: HistoryEntryReplacement, ) { - let is_javascript = load_data.url.scheme() == "javascript"; - if is_javascript { - let window = self.documents.borrow().find_window(parent_pipeline_id); - if let Some(window) = window { - ScriptThread::eval_js_url(window.upcast::<GlobalScope>(), &mut load_data); - } - } - - match browsing_context_id { - Some(browsing_context_id) => { - let iframe = self - .documents - .borrow() - .find_iframe(parent_pipeline_id, browsing_context_id); - if let Some(iframe) = iframe { - iframe.navigate_or_reload_child_browsing_context( - Some(load_data), - NavigationType::Regular, - replace, - ); - } - }, - None => { - self.script_sender - .send((parent_pipeline_id, ScriptMsg::LoadUrl(load_data, replace))) - .unwrap(); - }, + let iframe = self + .documents + .borrow() + .find_iframe(parent_pipeline_id, browsing_context_id); + if let Some(iframe) = iframe { + iframe.navigate_or_reload_child_browsing_context( + load_data, + NavigationType::Regular, + replace, + ); } } + /// Turn javascript: URL into JS code to eval, according to the steps in + /// https://html.spec.whatwg.org/multipage/#javascript-protocol pub fn eval_js_url(global_scope: &GlobalScope, load_data: &mut LoadData) { - // Turn javascript: URL into JS code to eval, according to the steps in - // https://html.spec.whatwg.org/multipage/#javascript-protocol - // This slice of the URL’s serialization is equivalent to (5.) to (7.): // Start with the scheme data of the parsed URL; // append question mark and query component, if any; |