Check CSP for javascript: URLs (#36709)

Also update a WPT test to fail-fast if the iframe incorrectly
evaluates the `eval`. Before, it would run into a timeout if
the implementation is correct. Now we reject the promise
when an exception is thrown.

Requires servo/rust-content-security-policy#6

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-05-02 22:13:31 +02:00 committed by GitHub
parent b8971e528f
commit dd63325f50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 70 additions and 57 deletions

View file

@ -18,11 +18,12 @@ use base::id::{
ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId,
};
use constellation_traits::{
BlobData, BlobImpl, BroadcastMsg, FileBlob, MessagePortImpl, MessagePortMsg, PortMessageTask,
ScriptToConstellationChan, ScriptToConstellationMessage,
BlobData, BlobImpl, BroadcastMsg, FileBlob, LoadData, LoadOrigin, MessagePortImpl,
MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage,
};
use content_security_policy::{
CheckResult, CspList, PolicyDisposition, PolicySource, Violation, ViolationResource,
CheckResult, CspList, Destination, Initiator, NavigationCheckType, ParserMetadata,
PolicyDisposition, PolicySource, Request, Violation, ViolationResource,
};
use crossbeam_channel::Sender;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
@ -62,6 +63,7 @@ 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::{TimerEventId, TimerEventRequest, TimerSource};
use url::Origin;
use uuid::Uuid;
#[cfg(feature = "webgpu")]
use webgpu_traits::{DeviceLostReason, WebGPUDevice};
@ -2951,6 +2953,33 @@ impl GlobalScope {
is_js_evaluation_allowed == CheckResult::Allowed
}
pub(crate) fn should_navigation_request_be_blocked(&self, load_data: &LoadData) -> 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);
result == CheckResult::Blocked
}
pub(crate) fn create_image_bitmap(
&self,
image: ImageBitmapSource,

View file

@ -162,8 +162,13 @@ 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)
{
return;
}
// 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 ScriptThread::check_load_origin(&load_data.load_origin, &document.url().origin())
{
ScriptThread::eval_js_url(&window_proxy.global(), &mut load_data, can_gc);