mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Report URI with POST fetch request
Part of #4577 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
parent
c94605b13e
commit
b1deeec015
21 changed files with 277 additions and 109 deletions
|
@ -31,8 +31,6 @@ use http::{HeaderName, Method, StatusCode};
|
||||||
use http_body_util::combinators::BoxBody;
|
use http_body_util::combinators::BoxBody;
|
||||||
use hyper::body::{Body, Bytes, Incoming};
|
use hyper::body::{Body, Bytes, Incoming};
|
||||||
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
use hyper::{Request as HyperRequest, Response as HyperResponse};
|
||||||
use ipc_channel::ipc::{self, IpcSharedMemory};
|
|
||||||
use ipc_channel::router::ROUTER;
|
|
||||||
use net::cookie::ServoCookie;
|
use net::cookie::ServoCookie;
|
||||||
use net::cookie_storage::CookieStorage;
|
use net::cookie_storage::CookieStorage;
|
||||||
use net::fetch::methods::{self};
|
use net::fetch::methods::{self};
|
||||||
|
@ -41,8 +39,8 @@ use net::resource_thread::AuthCacheEntry;
|
||||||
use net::test::{DECODER_BUFFER_SIZE, replace_host_table};
|
use net::test::{DECODER_BUFFER_SIZE, replace_host_table};
|
||||||
use net_traits::http_status::HttpStatus;
|
use net_traits::http_status::HttpStatus;
|
||||||
use net_traits::request::{
|
use net_traits::request::{
|
||||||
BodyChunkRequest, BodyChunkResponse, BodySource, CredentialsMode, Destination, Referrer,
|
CredentialsMode, Destination, Referrer, Request, RequestBuilder, RequestMode,
|
||||||
Request, RequestBody, RequestBuilder, RequestMode,
|
create_request_body_with_content,
|
||||||
};
|
};
|
||||||
use net_traits::response::{Response, ResponseBody};
|
use net_traits::response::{Response, ResponseBody};
|
||||||
use net_traits::{CookieSource, FetchTaskTarget, NetworkError, ReferrerPolicy};
|
use net_traits::{CookieSource, FetchTaskTarget, NetworkError, ReferrerPolicy};
|
||||||
|
@ -100,24 +98,6 @@ pub fn expect_devtools_http_response(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_request_body_with_content(content: IpcSharedMemory) -> RequestBody {
|
|
||||||
let content_len = content.len();
|
|
||||||
|
|
||||||
let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
|
|
||||||
ROUTER.add_typed_route(
|
|
||||||
chunk_request_receiver,
|
|
||||||
Box::new(move |message| {
|
|
||||||
let request = message.unwrap();
|
|
||||||
if let BodyChunkRequest::Connect(sender) = request {
|
|
||||||
let _ = sender.send(BodyChunkResponse::Chunk(content.clone()));
|
|
||||||
let _ = sender.send(BodyChunkResponse::Done);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_default_headers_loaded_in_every_request() {
|
fn test_check_default_headers_loaded_in_every_request() {
|
||||||
let expected_headers = Arc::new(Mutex::new(None));
|
let expected_headers = Arc::new(Mutex::new(None));
|
||||||
|
@ -591,8 +571,8 @@ fn test_load_doesnt_send_request_body_on_any_redirect() {
|
||||||
};
|
};
|
||||||
let (pre_server, pre_url) = make_server(pre_handler);
|
let (pre_server, pre_url) = make_server(pre_handler);
|
||||||
|
|
||||||
let content = b"Body on POST!";
|
let content = "Body on POST!";
|
||||||
let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content));
|
let request_body = create_request_body_with_content(content);
|
||||||
|
|
||||||
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
|
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
|
||||||
.body(Some(request_body))
|
.body(Some(request_body))
|
||||||
|
@ -891,20 +871,21 @@ fn test_when_cookie_received_marked_secure_is_ignored_for_http() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_sets_content_length_to_length_of_request_body() {
|
fn test_load_sets_content_length_to_length_of_request_body() {
|
||||||
let content = b"This is a request body";
|
let content = "This is a request body";
|
||||||
|
let content_bytes = content.as_bytes();
|
||||||
let handler =
|
let handler =
|
||||||
move |request: HyperRequest<Incoming>,
|
move |request: HyperRequest<Incoming>,
|
||||||
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
response: &mut HyperResponse<BoxBody<Bytes, hyper::Error>>| {
|
||||||
let content_length = ContentLength(content.len() as u64);
|
let content_length = ContentLength(content_bytes.len() as u64);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
request.headers().typed_get::<ContentLength>(),
|
request.headers().typed_get::<ContentLength>(),
|
||||||
Some(content_length)
|
Some(content_length)
|
||||||
);
|
);
|
||||||
*response.body_mut() = make_body(content.to_vec());
|
*response.body_mut() = make_body(content_bytes.to_vec());
|
||||||
};
|
};
|
||||||
let (server, url) = make_server(handler);
|
let (server, url) = make_server(handler);
|
||||||
|
|
||||||
let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content));
|
let request_body = create_request_body_with_content(content);
|
||||||
|
|
||||||
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
|
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
|
||||||
.method(Method::POST)
|
.method(Method::POST)
|
||||||
|
|
|
@ -3501,7 +3501,8 @@ impl GlobalScope {
|
||||||
Some(event_target) => Trusted::new(event_target.upcast()),
|
Some(event_target) => Trusted::new(event_target.upcast()),
|
||||||
};
|
};
|
||||||
// Step 3: Queue a task to run the following steps:
|
// Step 3: Queue a task to run the following steps:
|
||||||
let task = CSPViolationReportTask::new(Trusted::new(self), target, report);
|
let task =
|
||||||
|
CSPViolationReportTask::new(Trusted::new(self), target, report, violation.policy);
|
||||||
self.task_manager()
|
self.task_manager()
|
||||||
.dom_manipulation_task_source()
|
.dom_manipulation_task_source()
|
||||||
.queue(task);
|
.queue(task);
|
||||||
|
|
|
@ -2,7 +2,16 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use net_traits::request::Referrer;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use content_security_policy as csp;
|
||||||
|
use headers::{ContentType, HeaderMap, HeaderMapExt};
|
||||||
|
use net_traits::request::{
|
||||||
|
CredentialsMode, Referrer, RequestBody, RequestId, create_request_body_with_content,
|
||||||
|
};
|
||||||
|
use net_traits::{
|
||||||
|
FetchMetadata, FetchResponseListener, NetworkError, ResourceFetchTiming, ResourceTimingType,
|
||||||
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use stylo_atoms::Atom;
|
use stylo_atoms::Atom;
|
||||||
|
@ -14,10 +23,14 @@ use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding
|
||||||
};
|
};
|
||||||
use crate::dom::bindings::inheritance::Castable;
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
use crate::dom::bindings::refcounted::Trusted;
|
use crate::dom::bindings::refcounted::Trusted;
|
||||||
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
|
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
|
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||||
use crate::dom::securitypolicyviolationevent::SecurityPolicyViolationEvent;
|
use crate::dom::securitypolicyviolationevent::SecurityPolicyViolationEvent;
|
||||||
use crate::dom::types::GlobalScope;
|
use crate::dom::types::GlobalScope;
|
||||||
|
use crate::fetch::create_a_potential_cors_request;
|
||||||
|
use crate::network_listener::{PreInvoke, ResourceTimingListener, submit_timing};
|
||||||
use crate::script_runtime::CanGc;
|
use crate::script_runtime::CanGc;
|
||||||
use crate::task::TaskOnce;
|
use crate::task::TaskOnce;
|
||||||
|
|
||||||
|
@ -25,9 +38,10 @@ pub(crate) struct CSPViolationReportTask {
|
||||||
global: Trusted<GlobalScope>,
|
global: Trusted<GlobalScope>,
|
||||||
event_target: Trusted<EventTarget>,
|
event_target: Trusted<EventTarget>,
|
||||||
violation_report: SecurityPolicyViolationReport,
|
violation_report: SecurityPolicyViolationReport,
|
||||||
|
violation_policy: csp::Policy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct SecurityPolicyViolationReport {
|
pub(crate) struct SecurityPolicyViolationReport {
|
||||||
sample: Option<String>,
|
sample: Option<String>,
|
||||||
|
@ -47,6 +61,30 @@ pub(crate) struct SecurityPolicyViolationReport {
|
||||||
disposition: SecurityPolicyViolationEventDisposition,
|
disposition: SecurityPolicyViolationEventDisposition,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct CSPReportUriViolationReportBody {
|
||||||
|
document_uri: String,
|
||||||
|
referrer: String,
|
||||||
|
blocked_uri: String,
|
||||||
|
effective_directive: String,
|
||||||
|
violated_directive: String,
|
||||||
|
original_policy: String,
|
||||||
|
#[serde(serialize_with = "serialize_disposition")]
|
||||||
|
disposition: SecurityPolicyViolationEventDisposition,
|
||||||
|
status_code: u16,
|
||||||
|
script_sample: Option<String>,
|
||||||
|
source_file: Option<String>,
|
||||||
|
line_number: Option<u32>,
|
||||||
|
column_number: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct CSPReportUriViolationReport {
|
||||||
|
csp_report: CSPReportUriViolationReportBody,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct CSPViolationReportBuilder {
|
pub(crate) struct CSPViolationReportBuilder {
|
||||||
pub report_only: bool,
|
pub report_only: bool,
|
||||||
|
@ -114,36 +152,19 @@ impl CSPViolationReportBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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 url’s scheme is not an HTTP(S) scheme, then return url’s scheme.
|
|
||||||
if scheme != "https" && scheme != "http" {
|
|
||||||
return scheme.to_owned();
|
|
||||||
}
|
|
||||||
// > Step 2: Set url’s fragment to the empty string.
|
|
||||||
url.set_fragment(None);
|
|
||||||
// > Step 3: Set url’s username to the empty string.
|
|
||||||
let _ = url.set_username("");
|
|
||||||
// > Step 4: Set url’s 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self, global: &GlobalScope) -> SecurityPolicyViolationReport {
|
pub fn build(self, global: &GlobalScope) -> SecurityPolicyViolationReport {
|
||||||
SecurityPolicyViolationReport {
|
SecurityPolicyViolationReport {
|
||||||
violated_directive: self.effective_directive.clone(),
|
violated_directive: self.effective_directive.clone(),
|
||||||
effective_directive: self.effective_directive.clone(),
|
effective_directive: self.effective_directive.clone(),
|
||||||
document_url: self.strip_url_for_reports(global.get_url()),
|
document_url: strip_url_for_reports(global.get_url()),
|
||||||
disposition: match self.report_only {
|
disposition: match self.report_only {
|
||||||
true => SecurityPolicyViolationEventDisposition::Report,
|
true => SecurityPolicyViolationEventDisposition::Report,
|
||||||
false => SecurityPolicyViolationEventDisposition::Enforce,
|
false => SecurityPolicyViolationEventDisposition::Enforce,
|
||||||
},
|
},
|
||||||
// https://w3c.github.io/webappsec-csp/#violation-referrer
|
// https://w3c.github.io/webappsec-csp/#violation-referrer
|
||||||
referrer: match global.get_referrer() {
|
referrer: match global.get_referrer() {
|
||||||
Referrer::Client(url) => self.strip_url_for_reports(url),
|
Referrer::Client(url) => strip_url_for_reports(url),
|
||||||
Referrer::ReferrerUrl(url) => self.strip_url_for_reports(url),
|
Referrer::ReferrerUrl(url) => strip_url_for_reports(url),
|
||||||
_ => "".to_owned(),
|
_ => "".to_owned(),
|
||||||
},
|
},
|
||||||
sample: self.sample,
|
sample: self.sample,
|
||||||
|
@ -162,22 +183,24 @@ impl CSPViolationReportTask {
|
||||||
global: Trusted<GlobalScope>,
|
global: Trusted<GlobalScope>,
|
||||||
event_target: Trusted<EventTarget>,
|
event_target: Trusted<EventTarget>,
|
||||||
violation_report: SecurityPolicyViolationReport,
|
violation_report: SecurityPolicyViolationReport,
|
||||||
|
violation_policy: csp::Policy,
|
||||||
) -> CSPViolationReportTask {
|
) -> CSPViolationReportTask {
|
||||||
CSPViolationReportTask {
|
CSPViolationReportTask {
|
||||||
global,
|
global,
|
||||||
event_target,
|
event_target,
|
||||||
violation_report,
|
violation_report,
|
||||||
|
violation_policy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fire_violation_event(self, can_gc: CanGc) {
|
fn fire_violation_event(&self, can_gc: CanGc) {
|
||||||
let event = SecurityPolicyViolationEvent::new(
|
let event = SecurityPolicyViolationEvent::new(
|
||||||
&self.global.root(),
|
&self.global.root(),
|
||||||
Atom::from("securitypolicyviolation"),
|
Atom::from("securitypolicyviolation"),
|
||||||
EventBubbles::Bubbles,
|
EventBubbles::Bubbles,
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
EventComposed::Composed,
|
EventComposed::Composed,
|
||||||
&self.violation_report.convert(),
|
&self.violation_report.clone().convert(),
|
||||||
can_gc,
|
can_gc,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -185,6 +208,72 @@ impl CSPViolationReportTask {
|
||||||
.upcast::<Event>()
|
.upcast::<Event>()
|
||||||
.fire(&self.event_target.root(), can_gc);
|
.fire(&self.event_target.root(), can_gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://www.w3.org/TR/CSP/#deprecated-serialize-violation>
|
||||||
|
fn serialize_violation(&self) -> Option<RequestBody> {
|
||||||
|
let report_body = CSPReportUriViolationReport {
|
||||||
|
// Steps 1-3.
|
||||||
|
csp_report: self.violation_report.clone().into(),
|
||||||
|
};
|
||||||
|
// Step 4. Return the result of serialize an infra value to JSON bytes given «[ "csp-report" → body ]».
|
||||||
|
Some(create_request_body_with_content(
|
||||||
|
&serde_json::to_string(&report_body).unwrap_or("".to_owned()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Step 3.4 of <https://www.w3.org/TR/CSP/#report-violation>
|
||||||
|
fn post_csp_violation_to_report_uri(&self, report_uri_directive: &csp::Directive) {
|
||||||
|
let global = self.global.root();
|
||||||
|
// Step 3.4.1. If violation’s policy’s directive set contains a directive named
|
||||||
|
// "report-to", skip the remaining substeps.
|
||||||
|
if self
|
||||||
|
.violation_policy
|
||||||
|
.contains_a_directive_whose_name_is("report-to")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Step 3.4.2. For each token of directive’s value:
|
||||||
|
for token in &report_uri_directive.value {
|
||||||
|
// Step 3.4.2.1. Let endpoint be the result of executing the URL parser with token as the input,
|
||||||
|
// and violation’s url as the base URL.
|
||||||
|
let Ok(endpoint) = ServoUrl::parse_with_base(Some(&global.get_url()), token) else {
|
||||||
|
// Step 3.4.2.2. If endpoint is not a valid URL, skip the remaining substeps.
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// Step 3.4.2.3. Let request be a new request, initialized as follows:
|
||||||
|
let mut headers = HeaderMap::with_capacity(1);
|
||||||
|
headers.typed_insert(ContentType::from(
|
||||||
|
"application/csp-report".parse::<mime::Mime>().unwrap(),
|
||||||
|
));
|
||||||
|
let request_body = self.serialize_violation();
|
||||||
|
let request = create_a_potential_cors_request(
|
||||||
|
None,
|
||||||
|
endpoint.clone(),
|
||||||
|
csp::Destination::Report,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
global.get_referrer(),
|
||||||
|
global.insecure_requests_policy(),
|
||||||
|
global.has_trustworthy_ancestor_or_current_origin(),
|
||||||
|
global.policy_container(),
|
||||||
|
)
|
||||||
|
.method(http::Method::POST)
|
||||||
|
.body(request_body)
|
||||||
|
.origin(global.origin().immutable().clone())
|
||||||
|
.credentials_mode(CredentialsMode::CredentialsSameOrigin)
|
||||||
|
.headers(headers);
|
||||||
|
// Step 3.4.2.4. Fetch request. The result will be ignored.
|
||||||
|
global.fetch(
|
||||||
|
request,
|
||||||
|
Arc::new(Mutex::new(CSPReportUriFetchListener {
|
||||||
|
endpoint,
|
||||||
|
global: Trusted::new(&global),
|
||||||
|
resource_timing: ResourceFetchTiming::new(ResourceTimingType::None),
|
||||||
|
})),
|
||||||
|
global.task_manager().networking_task_source().into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Corresponds to the operation in 5.5 Report Violation
|
/// Corresponds to the operation in 5.5 Report Violation
|
||||||
|
@ -196,7 +285,15 @@ impl TaskOnce for CSPViolationReportTask {
|
||||||
// > that uses the SecurityPolicyViolationEvent interface
|
// > that uses the SecurityPolicyViolationEvent interface
|
||||||
// > at target with its attributes initialized as follows:
|
// > at target with its attributes initialized as follows:
|
||||||
self.fire_violation_event(CanGc::note());
|
self.fire_violation_event(CanGc::note());
|
||||||
// TODO: Support `report-to` directive that corresponds to 5.5.3.5.
|
// Step 3.4. If violation’s policy’s directive set contains a directive named "report-uri" directive:
|
||||||
|
if let Some(report_uri_directive) = self
|
||||||
|
.violation_policy
|
||||||
|
.directive_set
|
||||||
|
.iter()
|
||||||
|
.find(|directive| directive.name == "report-uri")
|
||||||
|
{
|
||||||
|
self.post_csp_violation_to_report_uri(report_uri_directive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +317,62 @@ impl Convert<SecurityPolicyViolationEventInit> for SecurityPolicyViolationReport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://www.w3.org/TR/CSP/#deprecated-serialize-violation>
|
||||||
|
impl From<SecurityPolicyViolationReport> for CSPReportUriViolationReportBody {
|
||||||
|
fn from(value: SecurityPolicyViolationReport) -> Self {
|
||||||
|
// Step 1. Let body be a map with its keys initialized as follows:
|
||||||
|
let mut converted = Self {
|
||||||
|
document_uri: value.document_url,
|
||||||
|
referrer: value.referrer,
|
||||||
|
blocked_uri: value.blocked_url,
|
||||||
|
effective_directive: value.effective_directive,
|
||||||
|
violated_directive: value.violated_directive,
|
||||||
|
original_policy: value.original_policy,
|
||||||
|
disposition: value.disposition,
|
||||||
|
status_code: value.status_code,
|
||||||
|
script_sample: None,
|
||||||
|
source_file: None,
|
||||||
|
line_number: None,
|
||||||
|
column_number: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 2. If violation’s source file is not null:
|
||||||
|
if !value.source_file.is_empty() {
|
||||||
|
// Step 2.1. Set body["source-file'] to the result of
|
||||||
|
// executing § 5.4 Strip URL for use in reports on violation’s source file.
|
||||||
|
converted.source_file = ServoUrl::parse(&value.source_file)
|
||||||
|
.map(strip_url_for_reports)
|
||||||
|
.ok();
|
||||||
|
// Step 2.2. Set body["line-number"] to violation’s line number.
|
||||||
|
converted.line_number = Some(value.line_number);
|
||||||
|
// Step 2.3. Set body["column-number"] to violation’s column number.
|
||||||
|
converted.column_number = Some(value.column_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3. Assert: If body["blocked-uri"] is not "inline", then body["sample"] is the empty string.
|
||||||
|
debug_assert!(converted.blocked_uri == "inline" || converted.script_sample.is_none());
|
||||||
|
|
||||||
|
converted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports>
|
||||||
|
fn strip_url_for_reports(mut url: ServoUrl) -> String {
|
||||||
|
let scheme = url.scheme();
|
||||||
|
// > Step 1: If url’s scheme is not an HTTP(S) scheme, then return url’s scheme.
|
||||||
|
if scheme != "https" && scheme != "http" {
|
||||||
|
return scheme.to_owned();
|
||||||
|
}
|
||||||
|
// > Step 2: Set url’s fragment to the empty string.
|
||||||
|
url.set_fragment(None);
|
||||||
|
// > Step 3: Set url’s username to the empty string.
|
||||||
|
let _ = url.set_username("");
|
||||||
|
// > Step 4: Set url’s 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()
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize_disposition<S: serde::Serializer>(
|
fn serialize_disposition<S: serde::Serializer>(
|
||||||
val: &SecurityPolicyViolationEventDisposition,
|
val: &SecurityPolicyViolationEventDisposition,
|
||||||
serializer: S,
|
serializer: S,
|
||||||
|
@ -229,3 +382,71 @@ fn serialize_disposition<S: serde::Serializer>(
|
||||||
SecurityPolicyViolationEventDisposition::Enforce => serializer.serialize_str("enforce"),
|
SecurityPolicyViolationEventDisposition::Enforce => serializer.serialize_str("enforce"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CSPReportUriFetchListener {
|
||||||
|
/// Endpoint URL of this request.
|
||||||
|
endpoint: ServoUrl,
|
||||||
|
/// Timing data for this resource.
|
||||||
|
resource_timing: ResourceFetchTiming,
|
||||||
|
/// The global object fetching the report uri violation
|
||||||
|
global: Trusted<GlobalScope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FetchResponseListener for CSPReportUriFetchListener {
|
||||||
|
fn process_request_body(&mut self, _: RequestId) {}
|
||||||
|
|
||||||
|
fn process_request_eof(&mut self, _: RequestId) {}
|
||||||
|
|
||||||
|
fn process_response(
|
||||||
|
&mut self,
|
||||||
|
_: RequestId,
|
||||||
|
fetch_metadata: Result<FetchMetadata, NetworkError>,
|
||||||
|
) {
|
||||||
|
_ = fetch_metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
|
||||||
|
_ = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_response_eof(
|
||||||
|
&mut self,
|
||||||
|
_: RequestId,
|
||||||
|
response: Result<ResourceFetchTiming, NetworkError>,
|
||||||
|
) {
|
||||||
|
_ = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
|
||||||
|
&mut self.resource_timing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing(&self) -> &ResourceFetchTiming {
|
||||||
|
&self.resource_timing
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_resource_timing(&mut self) {
|
||||||
|
submit_timing(self, CanGc::note())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
|
||||||
|
let global = &self.resource_timing_global();
|
||||||
|
global.report_csp_violations(violations, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceTimingListener for CSPReportUriFetchListener {
|
||||||
|
fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
|
||||||
|
(InitiatorType::Other, self.endpoint.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
|
||||||
|
self.global.root()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreInvoke for CSPReportUriFetchListener {
|
||||||
|
fn should_invoke(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use content_security_policy::{self as csp};
|
||||||
use http::header::{AUTHORIZATION, HeaderName};
|
use http::header::{AUTHORIZATION, HeaderName};
|
||||||
use http::{HeaderMap, Method};
|
use http::{HeaderMap, Method};
|
||||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
|
use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
|
||||||
|
use ipc_channel::router::ROUTER;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -925,3 +926,22 @@ pub fn convert_header_names_to_sorted_lowercase_set(
|
||||||
ordered_set.dedup();
|
ordered_set.dedup();
|
||||||
ordered_set.into_iter().cloned().collect()
|
ordered_set.into_iter().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_request_body_with_content(content: &str) -> RequestBody {
|
||||||
|
let content_bytes = IpcSharedMemory::from_bytes(content.as_bytes());
|
||||||
|
let content_len = content_bytes.len();
|
||||||
|
|
||||||
|
let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
|
||||||
|
ROUTER.add_typed_route(
|
||||||
|
chunk_request_receiver,
|
||||||
|
Box::new(move |message| {
|
||||||
|
let request = message.unwrap();
|
||||||
|
if let BodyChunkRequest::Connect(sender) = request {
|
||||||
|
let _ = sender.send(BodyChunkResponse::Chunk(content_bytes.clone()));
|
||||||
|
let _ = sender.send(BodyChunkResponse::Done);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
RequestBody::new(chunk_request_sender, BodySource::Object, Some(content_len))
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-does-not-respect-base-uri.sub.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,7 +1,4 @@
|
||||||
[dedicatedworker-connect-src.html]
|
[dedicatedworker-connect-src.html]
|
||||||
[Reports match in http: with connect-src 'self']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Cross-origin 'fetch()' in blob: with connect-src 'self']
|
[Cross-origin 'fetch()' in blob: with connect-src 'self']
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-and-enforce.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-blocked-data-uri.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-blocked-uri-cross-origin.sub.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-blocked-uri.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-cross-origin-no-cookies.sub.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-original-url.sub.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,4 +0,0 @@
|
||||||
[report-preload-and-consume.https.html]
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Reporting endpoints received credentials.]
|
|
||||||
expected: TIMEOUT
|
|
|
@ -1,6 +0,0 @@
|
||||||
[report-same-origin-with-cookies.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test report cookies.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-effective-directive.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-from-child-frame.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-from-inline-javascript.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-from-javascript.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-multiple-reversed.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-multiple.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[report-uri-scheme-relative.html]
|
|
||||||
[Violation report status OK.]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue