mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Propagate parent policy container to local iframes (#36710)
This follows the rules as defined in https://w3c.github.io/webappsec-csp/#security-inherit-csp where local iframes (about:blank and about:srcdoc) should initially start with the CSP rules of the parent. After that, all new CSP headers should only be set on the policy container of the iframe. Part of #36437 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com> Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
parent
4164f76769
commit
539ca27284
11 changed files with 45 additions and 47 deletions
|
@ -279,6 +279,7 @@ impl HTMLIFrameElement {
|
||||||
Some(document.insecure_requests_policy()),
|
Some(document.insecure_requests_policy()),
|
||||||
document.has_trustworthy_ancestor_or_current_origin(),
|
document.has_trustworthy_ancestor_or_current_origin(),
|
||||||
);
|
);
|
||||||
|
load_data.policy_container = Some(window.as_global_scope().policy_container());
|
||||||
let element = self.upcast::<Element>();
|
let element = self.upcast::<Element>();
|
||||||
load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
|
load_data.srcdoc = String::from(element.get_string_attribute(&local_name!("srcdoc")));
|
||||||
self.navigate_or_reload_child_browsing_context(
|
self.navigate_or_reload_child_browsing_context(
|
||||||
|
@ -361,7 +362,7 @@ impl HTMLIFrameElement {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let load_data = LoadData::new(
|
let mut load_data = LoadData::new(
|
||||||
LoadOrigin::Script(document.origin().immutable().clone()),
|
LoadOrigin::Script(document.origin().immutable().clone()),
|
||||||
url,
|
url,
|
||||||
creator_pipeline_id,
|
creator_pipeline_id,
|
||||||
|
@ -378,6 +379,10 @@ impl HTMLIFrameElement {
|
||||||
let is_about_blank =
|
let is_about_blank =
|
||||||
pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
|
pipeline_id.is_some() && pipeline_id == self.about_blank_pipeline_id.get();
|
||||||
|
|
||||||
|
if is_about_blank {
|
||||||
|
load_data.policy_container = Some(window.as_global_scope().policy_container());
|
||||||
|
}
|
||||||
|
|
||||||
let history_handling = if is_about_blank {
|
let history_handling = if is_about_blank {
|
||||||
NavigationHistoryBehavior::Replace
|
NavigationHistoryBehavior::Replace
|
||||||
} else {
|
} else {
|
||||||
|
@ -407,7 +412,7 @@ impl HTMLIFrameElement {
|
||||||
let document = self.owner_document();
|
let document = self.owner_document();
|
||||||
let window = self.owner_window();
|
let window = self.owner_window();
|
||||||
let pipeline_id = Some(window.pipeline_id());
|
let pipeline_id = Some(window.pipeline_id());
|
||||||
let load_data = LoadData::new(
|
let mut load_data = LoadData::new(
|
||||||
LoadOrigin::Script(document.origin().immutable().clone()),
|
LoadOrigin::Script(document.origin().immutable().clone()),
|
||||||
url,
|
url,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
|
@ -417,6 +422,7 @@ impl HTMLIFrameElement {
|
||||||
Some(document.insecure_requests_policy()),
|
Some(document.insecure_requests_policy()),
|
||||||
document.has_trustworthy_ancestor_or_current_origin(),
|
document.has_trustworthy_ancestor_or_current_origin(),
|
||||||
);
|
);
|
||||||
|
load_data.policy_container = Some(window.as_global_scope().policy_container());
|
||||||
let browsing_context_id = BrowsingContextId::new();
|
let browsing_context_id = BrowsingContextId::new();
|
||||||
let webview_id = window.window_proxy().webview_id();
|
let webview_id = window.window_proxy().webview_id();
|
||||||
self.pipeline_id.set(None);
|
self.pipeline_id.set(None);
|
||||||
|
|
|
@ -21,6 +21,7 @@ use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, ns};
|
||||||
use hyper_serde::Serde;
|
use hyper_serde::Serde;
|
||||||
use markup5ever::TokenizerResult;
|
use markup5ever::TokenizerResult;
|
||||||
use mime::{self, Mime};
|
use mime::{self, Mime};
|
||||||
|
use net_traits::policy_container::PolicyContainer;
|
||||||
use net_traits::request::RequestId;
|
use net_traits::request::RequestId;
|
||||||
use net_traits::{
|
use net_traits::{
|
||||||
FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming,
|
FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming,
|
||||||
|
@ -813,6 +814,27 @@ impl ParserContext {
|
||||||
pushed_entry_index: None,
|
pushed_entry_index: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn append_parent_to_csp_list(&self, policy_container: Option<&PolicyContainer>) {
|
||||||
|
let Some(policy_container) = policy_container else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(parent_csp_list) = &policy_container.csp_list else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(parser) = self.parser.as_ref().map(|p| p.root()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let new_csp_list = match parser.document.get_csp_list() {
|
||||||
|
None => parent_csp_list.clone(),
|
||||||
|
Some(original_csp_list) => {
|
||||||
|
let mut appended_csp_list = original_csp_list.clone();
|
||||||
|
appended_csp_list.append(parent_csp_list.clone());
|
||||||
|
appended_csp_list.to_owned()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
parser.document.set_csp_list(Some(new_csp_list));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchResponseListener for ParserContext {
|
impl FetchResponseListener for ParserContext {
|
||||||
|
|
|
@ -3674,10 +3674,12 @@ impl ScriptThread {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let policy_container = incomplete.load_data.policy_container.clone();
|
||||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||||
|
|
||||||
let dummy_request_id = RequestId::default();
|
let dummy_request_id = RequestId::default();
|
||||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||||
|
context.append_parent_to_csp_list(policy_container.as_ref());
|
||||||
context.process_response_chunk(dummy_request_id, chunk);
|
context.process_response_chunk(dummy_request_id, chunk);
|
||||||
context.process_response_eof(
|
context.process_response_eof(
|
||||||
dummy_request_id,
|
dummy_request_id,
|
||||||
|
@ -3697,12 +3699,14 @@ impl ScriptThread {
|
||||||
let srcdoc = std::mem::take(&mut incomplete.load_data.srcdoc);
|
let srcdoc = std::mem::take(&mut incomplete.load_data.srcdoc);
|
||||||
let chunk = srcdoc.into_bytes();
|
let chunk = srcdoc.into_bytes();
|
||||||
|
|
||||||
|
let policy_container = incomplete.load_data.policy_container.clone();
|
||||||
self.incomplete_loads.borrow_mut().push(incomplete);
|
self.incomplete_loads.borrow_mut().push(incomplete);
|
||||||
|
|
||||||
let mut context = ParserContext::new(id, url);
|
let mut context = ParserContext::new(id, url);
|
||||||
let dummy_request_id = RequestId::default();
|
let dummy_request_id = RequestId::default();
|
||||||
|
|
||||||
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
context.process_response(dummy_request_id, Ok(FetchMetadata::Unfiltered(meta)));
|
||||||
|
context.append_parent_to_csp_list(policy_container.as_ref());
|
||||||
context.process_response_chunk(dummy_request_id, chunk);
|
context.process_response_chunk(dummy_request_id, chunk);
|
||||||
context.process_response_eof(
|
context.process_response_eof(
|
||||||
dummy_request_id,
|
dummy_request_id,
|
||||||
|
|
|
@ -22,6 +22,7 @@ use euclid::default::Size2D as UntypedSize2D;
|
||||||
use http::{HeaderMap, Method};
|
use http::{HeaderMap, Method};
|
||||||
use ipc_channel::Error as IpcError;
|
use ipc_channel::Error as IpcError;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
|
use net_traits::policy_container::PolicyContainer;
|
||||||
use net_traits::request::{InsecureRequestsPolicy, Referrer, RequestBody};
|
use net_traits::request::{InsecureRequestsPolicy, Referrer, RequestBody};
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::{CoreResourceMsg, ReferrerPolicy, ResourceThreads};
|
use net_traits::{CoreResourceMsg, ReferrerPolicy, ResourceThreads};
|
||||||
|
@ -97,6 +98,8 @@ pub struct LoadData {
|
||||||
pub referrer: Referrer,
|
pub referrer: Referrer,
|
||||||
/// The referrer policy.
|
/// The referrer policy.
|
||||||
pub referrer_policy: ReferrerPolicy,
|
pub referrer_policy: ReferrerPolicy,
|
||||||
|
/// The policy container.
|
||||||
|
pub policy_container: Option<PolicyContainer>,
|
||||||
|
|
||||||
/// The source to use instead of a network response for a srcdoc document.
|
/// The source to use instead of a network response for a srcdoc document.
|
||||||
pub srcdoc: String,
|
pub srcdoc: String,
|
||||||
|
@ -143,6 +146,7 @@ impl LoadData {
|
||||||
js_eval_result: None,
|
js_eval_result: None,
|
||||||
referrer,
|
referrer,
|
||||||
referrer_policy,
|
referrer_policy,
|
||||||
|
policy_container: None,
|
||||||
srcdoc: "".to_string(),
|
srcdoc: "".to_string(),
|
||||||
inherited_secure_context,
|
inherited_secure_context,
|
||||||
crash: None,
|
crash: None,
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[document-write-iframe.html]
|
|
||||||
[document.open() keeps inherited CSPs on empty iframe.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,30 +1,6 @@
|
||||||
[iframe-all-local-schemes.sub.html]
|
[iframe-all-local-schemes.sub.html]
|
||||||
[<iframe>'s about:blank inherits policy.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[window about:blank inherits policy.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[<iframe srcdoc>'s inherits policy.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[<iframe src='blob:...'>'s inherits policy.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[window url='blob:...' inherits policy.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[<iframe src='data:...'>'s inherits policy.]
|
[<iframe src='data:...'>'s inherits policy.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[<iframe src='javascript:...'>'s inherits policy (static <img> is blocked)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[window url='javascript:...'>'s inherits policy (static <img> is blocked)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[<iframe src='javascript:...'>'s inherits policy (dynamically inserted <img> is blocked)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[<iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox)]
|
[<iframe sandbox src='blob:...'>'s inherits policy. (opaque origin sandbox)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
[iframe-srcdoc-inheritance.html]
|
[iframe-srcdoc-inheritance.html]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[First image should be blocked]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Second image should be blocked]
|
[Second image should be blocked]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
[location-reload.html]
|
[location-reload.html]
|
||||||
[location.reload() of empty iframe.]
|
expected: TIMEOUT
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[location.reload() of blob URL iframe.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[location.reload() of srcdoc iframe.]
|
[location.reload() of srcdoc iframe.]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
[to-javascript-parent-initiated-parent-csp.html]
|
[to-javascript-parent-initiated-parent-csp.html]
|
||||||
expected: TIMEOUT
|
expected: TIMEOUT
|
||||||
[Should not have executed the javascript url for\n iframe.contentWindow.location.href]
|
[Should not have executed the javascript url for\n iframe.contentWindow.location.href]
|
||||||
expected: FAIL
|
expected: TIMEOUT
|
||||||
|
|
||||||
[Should not have executed the javascript url for\n otherTab.location.href]
|
[Should not have executed the javascript url for\n otherTab.location.href]
|
||||||
expected: TIMEOUT
|
expected: NOTRUN
|
||||||
|
|
||||||
[Should not have executed the javascript url for\n area[target=iframe\].href]
|
[Should not have executed the javascript url for\n area[target=iframe\].href]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
@ -17,3 +17,6 @@
|
||||||
|
|
||||||
[Should not have executed the javascript url for\n a[target=iframe\].href]
|
[Should not have executed the javascript url for\n a[target=iframe\].href]
|
||||||
expected: NOTRUN
|
expected: NOTRUN
|
||||||
|
|
||||||
|
[Should not have executed the javascript url for\n iframe.src]
|
||||||
|
expected: NOTRUN
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[srcdoc-doesnt-bypass-script-src.sub.html]
|
|
||||||
[Expecting logs: ["violated-directive=script-src-elem"\]]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[eval-blocked-in-about-blank-iframe.html]
|
|
||||||
[eval-blocked-in-about-blank-iframe]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue