mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Update FetchTaskTarget to propagate CSP violations. (#36409)
It also updates the FetchResponseListener to process CSP violations to ensure that iframe elements (amongst others) properly generate the CSP events. These iframe elements are used in the Trusted Types tests themselves and weren't propagating the violations before. However, the tests themselves are still not passing since they also use Websockets, which currently aren't using the fetch machinery itself. That is fixed as part of [1]. [1]: https://github.com/servo/servo/issues/35028 --------- Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com> Signed-off-by: Josh Matthews <josh@joshmatthews.net> Co-authored-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
5d84acc06e
commit
85e4a2b5c7
146 changed files with 511 additions and 612 deletions
|
@ -173,9 +173,9 @@ pub async fn fetch_with_cors_cache(
|
|||
pub fn should_request_be_blocked_by_csp(
|
||||
request: &Request,
|
||||
policy_container: &PolicyContainer,
|
||||
) -> csp::CheckResult {
|
||||
) -> (csp::CheckResult, Vec<csp::Violation>) {
|
||||
let origin = match &request.origin {
|
||||
Origin::Client => return csp::CheckResult::Allowed,
|
||||
Origin::Client => return (csp::CheckResult::Allowed, Vec::new()),
|
||||
Origin::Origin(origin) => origin,
|
||||
};
|
||||
|
||||
|
@ -190,12 +190,11 @@ pub fn should_request_be_blocked_by_csp(
|
|||
parser_metadata: csp::ParserMetadata::None,
|
||||
};
|
||||
|
||||
// TODO: Instead of ignoring violations, report them.
|
||||
policy_container
|
||||
.csp_list
|
||||
.as_ref()
|
||||
.map(|c| c.should_request_be_blocked(&csp_request).0)
|
||||
.unwrap_or(csp::CheckResult::Allowed)
|
||||
.map(|c| c.should_request_be_blocked(&csp_request))
|
||||
.unwrap_or((csp::CheckResult::Allowed, Vec::new()))
|
||||
}
|
||||
|
||||
/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
|
||||
|
@ -278,7 +277,13 @@ pub async fn main_fetch(
|
|||
// Step 7. If should request be blocked due to a bad port, should fetching request be blocked
|
||||
// as mixed content, or should request be blocked by Content Security Policy returns blocked,
|
||||
// then set response to a network error.
|
||||
if should_request_be_blocked_by_csp(request, &policy_container) == csp::CheckResult::Blocked {
|
||||
let (check_result, violations) = should_request_be_blocked_by_csp(request, &policy_container);
|
||||
|
||||
if !violations.is_empty() {
|
||||
target.process_csp_violations(request, violations);
|
||||
}
|
||||
|
||||
if check_result == csp::CheckResult::Blocked {
|
||||
warn!("Request blocked by CSP");
|
||||
response = Some(Response::network_error(NetworkError::Internal(
|
||||
"Blocked by Content-Security-Policy".into(),
|
||||
|
|
|
@ -546,7 +546,8 @@ impl ImageCache for ImageCacheImpl {
|
|||
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg) {
|
||||
match (action, id) {
|
||||
(FetchResponseMsg::ProcessRequestBody(..), _) |
|
||||
(FetchResponseMsg::ProcessRequestEOF(..), _) => (),
|
||||
(FetchResponseMsg::ProcessRequestEOF(..), _) |
|
||||
(FetchResponseMsg::ProcessCspViolations(..), _) => (),
|
||||
(FetchResponseMsg::ProcessResponse(_, response), _) => {
|
||||
debug!("Received {:?} for {:?}", response.as_ref().map(|_| ()), id);
|
||||
let mut store = self.store.lock().unwrap();
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex, Weak};
|
|||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use base::id::TEST_PIPELINE_ID;
|
||||
use content_security_policy as csp;
|
||||
use crossbeam_channel::{Sender, unbounded};
|
||||
use devtools_traits::{HttpRequest as DevtoolsHttpRequest, HttpResponse as DevtoolsHttpResponse};
|
||||
use headers::{
|
||||
|
@ -163,6 +164,7 @@ fn test_fetch_blob() {
|
|||
assert_eq!(self.buffer, self.expected);
|
||||
let _ = self.sender.send(response.clone());
|
||||
}
|
||||
fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
|
||||
}
|
||||
|
||||
let context = new_fetch_context(None, None, None);
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||
use std::time::Duration;
|
||||
|
||||
use base::id::{TEST_PIPELINE_ID, TEST_WEBVIEW_ID};
|
||||
use content_security_policy as csp;
|
||||
use cookie::Cookie as CookiePair;
|
||||
use crossbeam_channel::{Receiver, unbounded};
|
||||
use devtools_traits::{
|
||||
|
@ -1537,6 +1538,7 @@ fn test_fetch_compressed_response_update_count() {
|
|||
fn process_response_eof(&mut self, _: &Request, _: &Response) {
|
||||
let _ = self.sender.take().unwrap().send(self.update_count);
|
||||
}
|
||||
fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
|
||||
}
|
||||
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel();
|
||||
|
|
|
@ -26,6 +26,7 @@ use std::net::TcpListener as StdTcpListener;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, LazyLock, Mutex, RwLock, Weak};
|
||||
|
||||
use content_security_policy as csp;
|
||||
use crossbeam_channel::{Receiver, Sender, unbounded};
|
||||
use devtools_traits::DevtoolsControlMsg;
|
||||
use embedder_traits::{AuthenticationResponse, EmbedderMsg, EmbedderProxy, EventLoopWaker};
|
||||
|
@ -196,6 +197,7 @@ impl FetchTaskTarget for FetchResponseCollector {
|
|||
fn process_response_eof(&mut self, _: &Request, response: &Response) {
|
||||
let _ = self.sender.take().unwrap().send(response.clone());
|
||||
}
|
||||
fn process_csp_violations(&mut self, _: &Request, _: Vec<csp::Violation>) {}
|
||||
}
|
||||
|
||||
fn fetch(request: Request, dc: Option<Sender<DevtoolsControlMsg>>) -> Response {
|
||||
|
|
|
@ -11,19 +11,22 @@
|
|||
//! over events from the network and events from the DOM, using async/await to avoid
|
||||
//! the need for a dedicated thread per websocket.
|
||||
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use async_tungstenite::WebSocketStream;
|
||||
use async_tungstenite::tokio::{ConnectStream, client_async_tls_with_connector_and_config};
|
||||
use base64::Engine;
|
||||
use content_security_policy as csp;
|
||||
use futures::future::TryFutureExt;
|
||||
use futures::stream::StreamExt;
|
||||
use http::header::{self, HeaderName, HeaderValue};
|
||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use log::{debug, trace, warn};
|
||||
use net_traits::request::{RequestBuilder, RequestMode};
|
||||
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
|
||||
use net_traits::request::{Origin, RequestBuilder, RequestMode};
|
||||
use net_traits::{CookieSource, MessageData, WebSocketDomAction, WebSocketNetworkEvent};
|
||||
use servo_url::ServoUrl;
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -39,7 +42,9 @@ use url::Url;
|
|||
use crate::async_runtime::HANDLE;
|
||||
use crate::connector::{CACertificates, TlsConfig, create_tls_config};
|
||||
use crate::cookie::ServoCookie;
|
||||
use crate::fetch::methods::should_request_be_blocked_due_to_a_bad_port;
|
||||
use crate::fetch::methods::{
|
||||
should_request_be_blocked_by_csp, should_request_be_blocked_due_to_a_bad_port,
|
||||
};
|
||||
use crate::hosts::replace_host;
|
||||
use crate::http_loader::HttpState;
|
||||
/// Create a tungstenite Request object for the initial HTTP request.
|
||||
|
@ -353,7 +358,7 @@ fn connect(
|
|||
ignore_certificate_errors: bool,
|
||||
) -> Result<(), String> {
|
||||
let protocols = match req_builder.mode {
|
||||
RequestMode::WebSocket { protocols } => protocols,
|
||||
RequestMode::WebSocket { ref mut protocols } => mem::take(protocols),
|
||||
_ => {
|
||||
return Err(
|
||||
"Received a RequestBuilder with a non-websocket mode in websocket_loader"
|
||||
|
@ -368,16 +373,36 @@ fn connect(
|
|||
.read()
|
||||
.unwrap()
|
||||
.apply_hsts_rules(&mut req_builder.url);
|
||||
let request = req_builder.build();
|
||||
|
||||
let req_url = req_builder.url.clone();
|
||||
let req_url = request.url();
|
||||
let req_origin = match request.origin {
|
||||
Origin::Client => unreachable!(),
|
||||
Origin::Origin(ref origin) => origin,
|
||||
};
|
||||
|
||||
if should_request_be_blocked_due_to_a_bad_port(&req_url) {
|
||||
return Err("Port blocked".to_string());
|
||||
}
|
||||
|
||||
let policy_container = match &request.policy_container {
|
||||
RequestPolicyContainer::Client => PolicyContainer::default(),
|
||||
RequestPolicyContainer::PolicyContainer(container) => container.to_owned(),
|
||||
};
|
||||
|
||||
let (check_result, violations) = should_request_be_blocked_by_csp(&request, &policy_container);
|
||||
|
||||
if !violations.is_empty() {
|
||||
let _ = resource_event_sender.send(WebSocketNetworkEvent::ReportCSPViolations(violations));
|
||||
}
|
||||
|
||||
if check_result == csp::CheckResult::Blocked {
|
||||
return Err("Blocked by Content-Security-Policy".to_string());
|
||||
}
|
||||
|
||||
let client = match create_request(
|
||||
&req_url,
|
||||
&req_builder.origin.ascii_serialization(),
|
||||
&req_origin.ascii_serialization(),
|
||||
&protocols,
|
||||
&http_state,
|
||||
) {
|
||||
|
@ -397,7 +422,7 @@ fn connect(
|
|||
Some(handle) => handle.spawn(
|
||||
start_websocket(
|
||||
http_state,
|
||||
req_builder.url.clone(),
|
||||
req_url.clone(),
|
||||
resource_event_sender,
|
||||
protocols,
|
||||
client,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue