servo/components/script/security_manager.rs
Josh Matthews c94ac5bccb
Move various reflector types and traits to script_bindings (#35279)
* script: Move Reflector to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Extract global() helper from DomObject into new trait. Move DomObject and related traits to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
2025-02-04 06:58:08 +00:00

180 lines
6.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use js::jsapi::RuntimeCode;
use net_traits::request::Referrer;
use serde::Serialize;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use crate::conversions::Convert;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit;
use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding::{
SecurityPolicyViolationEventDisposition, SecurityPolicyViolationEventInit,
};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::securitypolicyviolationevent::SecurityPolicyViolationEvent;
use crate::dom::types::GlobalScope;
use crate::script_runtime::CanGc;
use crate::task::TaskOnce;
pub(crate) struct CSPViolationReporter {
sample: Option<String>,
filename: String,
report_only: bool,
runtime_code: RuntimeCode,
line_number: u32,
column_number: u32,
target: Trusted<EventTarget>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SecurityPolicyViolationReport {
sample: Option<String>,
#[serde(rename = "blockedURL")]
blocked_url: String,
referrer: String,
status_code: u16,
#[serde(rename = "documentURL")]
document_url: String,
source_file: String,
violated_directive: String,
effective_directive: String,
line_number: u32,
column_number: u32,
original_policy: String,
disposition: SecurityPolicyViolationEventDisposition,
}
impl CSPViolationReporter {
pub(crate) fn new(
global: &GlobalScope,
sample: Option<String>,
report_only: bool,
runtime_code: RuntimeCode,
filename: String,
line_number: u32,
column_number: u32,
) -> CSPViolationReporter {
CSPViolationReporter {
sample,
filename,
report_only,
runtime_code,
line_number,
column_number,
target: Trusted::new(global.upcast::<EventTarget>()),
}
}
fn get_report(&self, global: &GlobalScope) -> SecurityPolicyViolationReport {
SecurityPolicyViolationReport {
sample: self.sample.clone(),
disposition: match self.report_only {
true => SecurityPolicyViolationEventDisposition::Report,
false => SecurityPolicyViolationEventDisposition::Enforce,
},
// https://w3c.github.io/webappsec-csp/#violation-resource
blocked_url: match self.runtime_code {
RuntimeCode::JS => "eval".to_owned(),
RuntimeCode::WASM => "wasm-eval".to_owned(),
},
// https://w3c.github.io/webappsec-csp/#violation-referrer
referrer: match global.get_referrer() {
Referrer::Client(url) => self.strip_url_for_reports(url),
Referrer::ReferrerUrl(url) => self.strip_url_for_reports(url),
_ => "".to_owned(),
},
status_code: global.status_code().unwrap_or(200),
document_url: self.strip_url_for_reports(global.get_url()),
source_file: self.filename.clone(),
violated_directive: "script-src".to_owned(),
effective_directive: "script-src".to_owned(),
line_number: self.line_number,
column_number: self.column_number,
original_policy: String::default(),
}
}
fn fire_violation_event(&self, can_gc: CanGc) {
let target = self.target.root();
let global = &target.global();
let report = self.get_report(global);
let event = SecurityPolicyViolationEvent::new(
global,
Atom::from("securitypolicyviolation"),
EventBubbles::Bubbles,
EventCancelable::Cancelable,
&report.convert(),
can_gc,
);
event.upcast::<Event>().fire(&target, can_gc);
}
/// <https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports>
fn strip_url_for_reports(&self, mut url: ServoUrl) -> String {
let scheme = url.scheme();
// > Step 1: If urls scheme is not an HTTP(S) scheme, then return urls scheme.
if scheme != "https" && scheme != "http" {
return scheme.to_owned();
}
// > Step 2: Set urls fragment to the empty string.
url.set_fragment(None);
// > Step 3: Set urls username to the empty string.
let _ = url.set_username("");
// > Step 4: Set urls password to the empty string.
let _ = url.set_password(None);
// > Step 5: Return the result of executing the URL serializer on url.
url.into_string()
}
}
/// Corresponds to the operation in 5.5 Report Violation
/// <https://w3c.github.io/webappsec-csp/#report-violation>
/// > Queue a task to run the following steps:
impl TaskOnce for CSPViolationReporter {
fn run_once(self) {
// > If target implements EventTarget, fire an event named securitypolicyviolation
// > that uses the SecurityPolicyViolationEvent interface
// > at target with its attributes initialized as follows:
self.fire_violation_event(CanGc::note());
// TODO: Support `report-to` directive that corresponds to 5.5.3.5.
}
}
impl Convert<SecurityPolicyViolationEventInit> for SecurityPolicyViolationReport {
fn convert(self) -> SecurityPolicyViolationEventInit {
SecurityPolicyViolationEventInit {
sample: self.sample.unwrap_or_default().into(),
blockedURI: self.blocked_url.into(),
referrer: self.referrer.into(),
statusCode: self.status_code,
documentURI: self.document_url.into(),
sourceFile: self.source_file.into(),
violatedDirective: self.violated_directive.into(),
effectiveDirective: self.effective_directive.into(),
lineNumber: self.line_number,
columnNumber: self.column_number,
originalPolicy: self.original_policy.into(),
disposition: self.disposition,
parent: EventInit::empty(),
}
}
}
impl Serialize for SecurityPolicyViolationEventDisposition {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Report => serializer.serialize_str("report"),
Self::Enforce => serializer.serialize_str("enforce"),
}
}
}