mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #26473 - Eijebong:cross-origin-resource-policy, r=jdm
Implement cross origin resource policy check Also fixes an img load event bug that was making some test very racey.
This commit is contained in:
commit
4eefaa4a2c
20 changed files with 138 additions and 148 deletions
|
@ -163,7 +163,7 @@ impl<'a> LayoutContext<'a> {
|
|||
}
|
||||
|
||||
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
|
||||
let image_info = WebRenderImageInfo::from_image(&*image);
|
||||
if image_info.key.is_none() {
|
||||
Some(image_info)
|
||||
|
|
|
@ -431,8 +431,8 @@ impl ImageFragmentInfo {
|
|||
layout_context
|
||||
.get_or_request_image_or_meta(node.opaque(), url, UsePlaceholder::Yes)
|
||||
.map(|result| match result {
|
||||
ImageOrMetadataAvailable::ImageAvailable(i, _) => {
|
||||
ImageOrMetadata::Image(i)
|
||||
ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
|
||||
ImageOrMetadata::Image(image)
|
||||
},
|
||||
ImageOrMetadataAvailable::MetadataAvailable(m) => {
|
||||
ImageOrMetadata::Metadata(m)
|
||||
|
|
|
@ -113,7 +113,7 @@ impl<'a> LayoutContext<'a> {
|
|||
}
|
||||
|
||||
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
|
||||
let image_info = WebRenderImageInfo {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
|
|
|
@ -125,7 +125,7 @@ impl ReplacedContent {
|
|||
image_url.clone(),
|
||||
UsePlaceholder::No,
|
||||
) {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => {
|
||||
Some(ImageOrMetadataAvailable::ImageAvailable { image, .. }) => {
|
||||
(Some(image.clone()), image.width as f32, image.height as f32)
|
||||
},
|
||||
Some(ImageOrMetadataAvailable::MetadataAvailable(metadata)) => {
|
||||
|
|
|
@ -33,6 +33,7 @@ use http::{HeaderMap, Request as HyperRequest};
|
|||
use hyper::{Body, Client, Method, Response as HyperResponse, StatusCode};
|
||||
use hyper_serde::Serde;
|
||||
use msg::constellation_msg::{HistoryStateId, PipelineId};
|
||||
use net_traits::pub_domains::reg_suffix;
|
||||
use net_traits::quality::{quality_to_value, Quality, QualityItem};
|
||||
use net_traits::request::Origin::Origin as SpecificOrigin;
|
||||
use net_traits::request::{is_cors_safelisted_method, is_cors_safelisted_request_header};
|
||||
|
@ -189,6 +190,28 @@ fn strict_origin_when_cross_origin(referrer_url: ServoUrl, url: ServoUrl) -> Opt
|
|||
strip_url(referrer_url, cross_origin)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#schemelessly-same-site
|
||||
fn is_schemelessy_same_site(site_a: &ImmutableOrigin, site_b: &ImmutableOrigin) -> bool {
|
||||
// Step 1
|
||||
if !site_a.is_tuple() && !site_b.is_tuple() && site_a == site_b {
|
||||
true
|
||||
} else if site_a.is_tuple() && site_b.is_tuple() {
|
||||
// Step 2.1
|
||||
let host_a = site_a.host().map(|h| h.to_string()).unwrap_or_default();
|
||||
let host_b = site_b.host().map(|h| h.to_string()).unwrap_or_default();
|
||||
|
||||
let host_a_reg = reg_suffix(&host_a);
|
||||
let host_b_reg = reg_suffix(&host_b);
|
||||
|
||||
// Step 2.2-2.3
|
||||
(site_a.host() == site_b.host() && host_a_reg == "") ||
|
||||
(host_a_reg == host_b_reg && host_a_reg != "")
|
||||
} else {
|
||||
// Step 3
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webappsec-referrer-policy/#strip-url>
|
||||
fn strip_url(mut referrer_url: ServoUrl, origin_only: bool) -> Option<ServoUrl> {
|
||||
const MAX_REFERRER_URL_LENGTH: usize = 4096;
|
||||
|
@ -1251,7 +1274,74 @@ fn http_network_or_cache_fetch(
|
|||
// TODO: if necessary set response's range-requested flag
|
||||
|
||||
// Step 9
|
||||
// TODO: handle CORS not set and cross-origin blocked
|
||||
// https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check
|
||||
#[derive(PartialEq)]
|
||||
enum CrossOriginResourcePolicy {
|
||||
Allowed,
|
||||
Blocked,
|
||||
}
|
||||
|
||||
fn cross_origin_resource_policy_check(
|
||||
request: &Request,
|
||||
response: &Response,
|
||||
) -> CrossOriginResourcePolicy {
|
||||
// Step 1
|
||||
if request.mode != RequestMode::NoCors {
|
||||
return CrossOriginResourcePolicy::Allowed;
|
||||
}
|
||||
|
||||
// Step 2
|
||||
let current_url_origin = request.current_url().origin();
|
||||
let same_origin = if let Origin::Origin(ref origin) = request.origin {
|
||||
*origin == request.current_url().origin()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if same_origin {
|
||||
return CrossOriginResourcePolicy::Allowed;
|
||||
}
|
||||
|
||||
// Step 3
|
||||
let policy = response
|
||||
.headers
|
||||
.get(HeaderName::from_static("cross-origin-resource-policy"))
|
||||
.map(|h| h.to_str().unwrap_or(""))
|
||||
.unwrap_or("");
|
||||
|
||||
// Step 4
|
||||
if policy == "same-origin" {
|
||||
return CrossOriginResourcePolicy::Blocked;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
if let Origin::Origin(ref request_origin) = request.origin {
|
||||
let schemeless_same_origin =
|
||||
is_schemelessy_same_site(&request_origin, ¤t_url_origin);
|
||||
if schemeless_same_origin &&
|
||||
(request_origin.scheme() == Some("https") ||
|
||||
response.https_state == HttpsState::None)
|
||||
{
|
||||
return CrossOriginResourcePolicy::Allowed;
|
||||
}
|
||||
};
|
||||
|
||||
// Step 6
|
||||
if policy == "same-site" {
|
||||
return CrossOriginResourcePolicy::Blocked;
|
||||
}
|
||||
|
||||
CrossOriginResourcePolicy::Allowed
|
||||
}
|
||||
|
||||
if http_request.response_tainting != ResponseTainting::CorsTainting &&
|
||||
cross_origin_resource_policy_check(&http_request, &response) ==
|
||||
CrossOriginResourcePolicy::Blocked
|
||||
{
|
||||
return Response::network_error(NetworkError::Internal(
|
||||
"Cross-origin resource policy check failed".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// Step 10
|
||||
// FIXME: Figure out what to do with request window objects
|
||||
|
|
|
@ -466,9 +466,12 @@ impl ImageCache for ImageCacheImpl {
|
|||
match result {
|
||||
Ok((image, image_url)) => {
|
||||
debug!("{} is available", url);
|
||||
return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(
|
||||
image, image_url,
|
||||
));
|
||||
let is_placeholder = image_url == store.placeholder_url;
|
||||
return ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
|
||||
image,
|
||||
url: image_url,
|
||||
is_placeholder,
|
||||
});
|
||||
},
|
||||
Err(()) => {
|
||||
debug!("{} is not available", url);
|
||||
|
@ -515,9 +518,14 @@ impl ImageCache for ImageCacheImpl {
|
|||
// TODO: make this behaviour configurable according to the caller's needs.
|
||||
store.handle_decoder(decoded);
|
||||
match store.get_completed_image_if_available(url, origin, cors_setting, use_placeholder) {
|
||||
Some(Ok((image, image_url))) => ImageCacheResult::Available(
|
||||
ImageOrMetadataAvailable::ImageAvailable(image, image_url),
|
||||
),
|
||||
Some(Ok((image, image_url))) => {
|
||||
let is_placeholder = image_url == store.placeholder_url;
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
|
||||
image,
|
||||
url: image_url,
|
||||
is_placeholder,
|
||||
})
|
||||
},
|
||||
_ => ImageCacheResult::LoadError,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,12 @@ use std::sync::Arc;
|
|||
/// Indicating either entire image or just metadata availability
|
||||
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub enum ImageOrMetadataAvailable {
|
||||
ImageAvailable(#[ignore_malloc_size_of = "Arc"] Arc<Image>, ServoUrl),
|
||||
ImageAvailable {
|
||||
#[ignore_malloc_size_of = "Arc"]
|
||||
image: Arc<Image>,
|
||||
url: ServoUrl,
|
||||
is_placeholder: bool,
|
||||
},
|
||||
MetadataAvailable(ImageMetadata),
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ pub enum CacheState {
|
|||
}
|
||||
|
||||
/// [Https state](https://fetch.spec.whatwg.org/#concept-response-https-state)
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
|
||||
pub enum HttpsState {
|
||||
None,
|
||||
Deprecated,
|
||||
|
|
|
@ -329,8 +329,16 @@ impl HTMLImageElement {
|
|||
);
|
||||
|
||||
match cache_result {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
|
||||
self.process_image_response(ImageResponse::Loaded(image, url))
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
|
||||
image,
|
||||
url,
|
||||
is_placeholder,
|
||||
}) => {
|
||||
if is_placeholder {
|
||||
self.process_image_response(ImageResponse::PlaceholderLoaded(image, url))
|
||||
} else {
|
||||
self.process_image_response(ImageResponse::Loaded(image, url))
|
||||
}
|
||||
},
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
|
||||
self.process_image_response(ImageResponse::MetadataLoaded(m))
|
||||
|
@ -1109,7 +1117,7 @@ impl HTMLImageElement {
|
|||
);
|
||||
|
||||
match cache_result {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(_image, _url)) => {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
|
||||
// Step 15
|
||||
self.finish_reacting_to_environment_change(
|
||||
selected_source,
|
||||
|
|
|
@ -166,8 +166,12 @@ impl HTMLVideoElement {
|
|||
);
|
||||
|
||||
match cache_result {
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable(img, url)) => {
|
||||
self.process_image_response(ImageResponse::Loaded(img, url));
|
||||
ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
|
||||
image,
|
||||
url,
|
||||
..
|
||||
}) => {
|
||||
self.process_image_response(ImageResponse::Loaded(image, url));
|
||||
},
|
||||
ImageCacheResult::ReadyForRequest(id) => {
|
||||
self.do_fetch_poster_frame(poster_url, id, cancel_receiver)
|
||||
|
|
|
@ -424608,7 +424608,7 @@
|
|||
]
|
||||
],
|
||||
"image-loads.html": [
|
||||
"8a0458f107abdf2b7d6664fb8194e6b4b0222989",
|
||||
"060b7551ea516837cf416c797e85474658857632",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
@ -424654,7 +424654,7 @@
|
|||
]
|
||||
],
|
||||
"script-loads.html": [
|
||||
"5850e0109f18c23e40d73686bef5e4b6a6b40686",
|
||||
"a9690fc70be13885d7ca6448730c83f755810774",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
|
|
@ -5,15 +5,3 @@
|
|||
[fetch-in-iframe]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin fetch in a data: iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin fetch in a cross origin iframe load fails if the server blocks cross-origin loads with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -7,53 +7,11 @@
|
|||
[fetch]
|
||||
expected: FAIL
|
||||
|
||||
[Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-scheme (HTTP to HTTPS) no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fetch.any.worker.html]
|
||||
[fetch]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-scheme (HTTP to HTTPS) no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fetch.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
|
|
@ -1,16 +1,4 @@
|
|||
[fetch.https.any.html]
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fetch.https.any.serviceworker.html]
|
||||
expected: ERROR
|
||||
|
@ -25,18 +13,6 @@
|
|||
|
||||
|
||||
[fetch.https.any.worker.html]
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[fetch]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
[iframe-loads.html]
|
||||
[Untitled]
|
||||
expected: FAIL
|
||||
[Load an iframe that has Cross-Origin-Resource-Policy header]
|
||||
expected: FAIL
|
||||
|
||||
[iframe-loads]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
[image-loads.html]
|
||||
[Same-origin image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin image load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin cors image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin cors image load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors image load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors image load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
[scheme-restriction.any.html]
|
||||
[Cross-Origin-Resource-Policy: same-site blocks retrieving HTTPS from HTTP]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[scheme-restriction.any.worker.html]
|
||||
[Cross-Origin-Resource-Policy: same-site blocks retrieving HTTPS from HTTP]
|
||||
expected: FAIL
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
[script-loads.html]
|
||||
[Same-origin script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin script load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin cors script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin cors script load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors script load with a 'Cross-Origin-Resource-Policy: same-origin' response header.]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin no-cors script load with a 'Cross-Origin-Resource-Policy: same-site' response header.]
|
||||
expected: FAIL
|
||||
|
|
@ -16,6 +16,7 @@ const noCors = false;
|
|||
|
||||
function loadImage(url, shoudLoad, corsMode, title)
|
||||
{
|
||||
const testDiv = document.getElementById("testDiv");
|
||||
promise_test(() => {
|
||||
const img = new Image();
|
||||
if (corsMode)
|
||||
|
|
|
@ -16,6 +16,7 @@ const noCors = false;
|
|||
|
||||
function loadScript(url, shoudLoad, corsMode, title)
|
||||
{
|
||||
const testDiv = document.getElementById("testDiv");
|
||||
promise_test(() => {
|
||||
const script = document.createElement("script");
|
||||
if (corsMode)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue