mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
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 <tvanderlippe@gmail.com> Signed-off-by: Tim van der Lippe <TimvdLippe@users.noreply.github.com>
This commit is contained in:
parent
2265570c88
commit
fc20d8b2e1
29 changed files with 424 additions and 289 deletions
|
@ -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!();
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/CSP/#initialize-document-csp>
|
||||
pub(crate) fn parse_csp_list_from_metadata(
|
||||
headers: &Option<Serde<HeaderMap>>,
|
||||
) -> Option<CspList> {
|
||||
// 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!();
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/CSP/#report-violation>
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn report_csp_violations(
|
||||
&self,
|
||||
violations: Vec<Violation>,
|
||||
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::<Window>() {
|
||||
// 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::<Node>().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::<Window>() {
|
||||
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()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue