mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #14865 - mrnayak:sri-fetch, r=jdm
Implement Subresource Integrity Implemented response validation part of https://w3c.github.io/webappsec-subresource-integrity/. Implemented step eighteen of the main fetch. If a request has integrity metadata, then following steps are performed 1) Wait for response body 2) If the response does not have a termination reason and response does not match request’s integrity metadata, set response and internalResponse to a network error. Dependency updated: html5ever-atoms from 0.1.2 to 0.1.3. This will not completely fix #14523, It will implement changes related to response validation. Request validation algorithm implementation needs CSP. I did not update any WPT-Test. In my local system, I found some assertion issue dependent on the order of execution of test-case. It would be helpful if someone could do "try" build on these changes to get wpt results. r? @jdm <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors <!-- Either: --> - [X] There are tests for these changes <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14865) <!-- Reviewable:end -->
This commit is contained in:
commit
f958dafcae
19 changed files with 439 additions and 260 deletions
|
@ -233,7 +233,6 @@ fn test_cors_preflight_fetch() {
|
|||
let _ = server.close();
|
||||
|
||||
assert!(!fetch_response.is_network_error());
|
||||
|
||||
match *fetch_response.body.lock().unwrap() {
|
||||
ResponseBody::Done(ref body) => assert_eq!(&**body, ACK),
|
||||
_ => panic!()
|
||||
|
@ -556,6 +555,53 @@ fn test_fetch_with_hsts() {
|
|||
"https");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch_with_sri_network_error() {
|
||||
static MESSAGE: &'static [u8] = b"alert('Hello, Network Error');";
|
||||
let handler = move |_: HyperRequest, response: HyperResponse| {
|
||||
response.send(MESSAGE).unwrap();
|
||||
};
|
||||
let (mut server, url) = make_server(handler);
|
||||
|
||||
let origin = Origin::Origin(url.origin());
|
||||
let mut request = Request::new(url, Some(origin), false, None);
|
||||
*request.referrer.borrow_mut() = Referrer::NoReferrer;
|
||||
// To calulate hash use :
|
||||
// echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A
|
||||
*request.integrity_metadata.borrow_mut() =
|
||||
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
|
||||
// Set the flag.
|
||||
request.local_urls_only = false;
|
||||
|
||||
let response = fetch(request, None);
|
||||
|
||||
let _ = server.close();
|
||||
assert!(response.is_network_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch_with_sri_sucess() {
|
||||
static MESSAGE: &'static [u8] = b"alert('Hello, world.');";
|
||||
let handler = move |_: HyperRequest, response: HyperResponse| {
|
||||
response.send(MESSAGE).unwrap();
|
||||
};
|
||||
let (mut server, url) = make_server(handler);
|
||||
|
||||
let origin = Origin::Origin(url.origin());
|
||||
let mut request = Request::new(url, Some(origin), false, None);
|
||||
*request.referrer.borrow_mut() = Referrer::NoReferrer;
|
||||
// To calulate hash use :
|
||||
// echo -n "alert('Hello, Network Error');" | openssl dgst -sha384 -binary | openssl base64 -A
|
||||
*request.integrity_metadata.borrow_mut() =
|
||||
"sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO".to_owned();
|
||||
// Set the flag.
|
||||
request.local_urls_only = false;
|
||||
|
||||
let response = fetch(request, None);
|
||||
|
||||
let _ = server.close();
|
||||
assert_eq!(response_is_done(&response), true);
|
||||
}
|
||||
fn setup_server_and_fetch(message: &'static [u8], redirect_cap: u32) -> Response {
|
||||
let handler = move |request: HyperRequest, mut response: HyperResponse| {
|
||||
let redirects = match request.uri {
|
||||
|
@ -742,7 +788,6 @@ fn test_fetch_async_returns_complete_response() {
|
|||
let fetch_response = fetch(request, None);
|
||||
|
||||
let _ = server.close();
|
||||
|
||||
assert_eq!(response_is_done(&fetch_response), true);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ extern crate url;
|
|||
#[cfg(test)] mod hsts;
|
||||
#[cfg(test)] mod http_loader;
|
||||
#[cfg(test)] mod filemanager_thread;
|
||||
#[cfg(test)] mod subresource_integrity;
|
||||
|
||||
use devtools_traits::DevtoolsControlMsg;
|
||||
use hyper::server::{Handler, Listening, Server};
|
||||
|
|
92
tests/unit/net/subresource_integrity.rs
Normal file
92
tests/unit/net/subresource_integrity.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use net::subresource_integrity::{SriEntry, get_prioritized_hash_function, get_strongest_metadata};
|
||||
use net::subresource_integrity::{is_response_integrity_valid, parsed_metadata};
|
||||
use net_traits::response::{Response, ResponseBody};
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
#[test]
|
||||
fn test_get_prioritized_hash_function() {
|
||||
let mut algorithm = get_prioritized_hash_function("sha256", "sha256");
|
||||
assert_eq!(algorithm, None);
|
||||
|
||||
algorithm = get_prioritized_hash_function("sha256", "sha384");
|
||||
assert_eq!(algorithm.unwrap(), "sha384");
|
||||
|
||||
algorithm = get_prioritized_hash_function("sha384", "sha512");
|
||||
assert_eq!(algorithm.unwrap(), "sha512");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsed_metadata_without_options() {
|
||||
let integrity_metadata = "sha384-Hash1";
|
||||
let ref parsed_metadata: SriEntry = parsed_metadata(integrity_metadata)[0];
|
||||
|
||||
assert_eq!(parsed_metadata.alg, "sha384");
|
||||
assert_eq!(parsed_metadata.val, "Hash1");
|
||||
assert!(parsed_metadata.opt.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsed_metadata_with_options() {
|
||||
let integrity_metadata = "sha384-Hash1?opt=23";
|
||||
let ref parsed_metadata: SriEntry = parsed_metadata(integrity_metadata)[0];
|
||||
|
||||
assert_eq!(parsed_metadata.alg, "sha384");
|
||||
assert_eq!(parsed_metadata.val, "Hash1");
|
||||
assert!(parsed_metadata.opt.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsed_metadata_with_malformed_integrity() {
|
||||
let integrity_metadata = "Not a valid integrity";
|
||||
let ref parsed_metadata_list: Vec<SriEntry> = parsed_metadata(integrity_metadata);
|
||||
|
||||
assert!(parsed_metadata_list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_strongest_metadata_two_same_algorithm() {
|
||||
let integrity_metadata = "sha512-Hash1 sha512-Hash2?opt=23";
|
||||
let parsed_metadata_list: Vec<SriEntry> = parsed_metadata(integrity_metadata);
|
||||
|
||||
let strong_metadata: Vec<SriEntry> = get_strongest_metadata(parsed_metadata_list);
|
||||
assert_eq!(strong_metadata.len(), 2);
|
||||
assert_eq!(strong_metadata[0].alg, strong_metadata[1].alg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_strongest_metadata_different_algorithm() {
|
||||
let integrity_metadata = "sha256-Hash0 sha384-Hash1 sha512-Hash2?opt=23";
|
||||
let parsed_metadata_list: Vec<SriEntry> = parsed_metadata(integrity_metadata);
|
||||
|
||||
let strong_metadata: Vec<SriEntry> = get_strongest_metadata(parsed_metadata_list);
|
||||
assert_eq!(strong_metadata.len(), 1);
|
||||
assert_eq!(strong_metadata[0].alg, "sha512");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_integrity_valid() {
|
||||
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
|
||||
let response: Response = Response::new(url);
|
||||
|
||||
let integrity_metadata = "sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
|
||||
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
|
||||
|
||||
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
|
||||
assert!(is_response_integrity_valid(integrity_metadata, &response));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_integrity_invalid() {
|
||||
let url: ServoUrl = ServoUrl::parse("http://servo.org").unwrap();
|
||||
let response: Response = Response::new(url);
|
||||
|
||||
let integrity_metadata = "sha256-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO";
|
||||
let response_body = "alert('Hello, world.');".to_owned().into_bytes();
|
||||
|
||||
*response.body.lock().unwrap() = ResponseBody::Done(response_body);
|
||||
assert!(!is_response_integrity_valid(integrity_metadata, &response));
|
||||
}
|
|
@ -43,6 +43,8 @@ skip: true
|
|||
skip: false
|
||||
[referrer-policy]
|
||||
skip: false
|
||||
[subresource-integrity]
|
||||
skip: false
|
||||
[touch-events]
|
||||
skip: false
|
||||
[typedarrays]
|
||||
|
|
|
@ -9870,18 +9870,12 @@
|
|||
[HTMLLinkElement interface: attribute nonce]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: attribute integrity]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: attribute referrerPolicy]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: document.createElement("link") must inherit property "nonce" with the proper type (5)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: document.createElement("link") must inherit property "integrity" with the proper type (6)]
|
||||
expected: FAIL
|
||||
|
||||
[HTMLLinkElement interface: document.createElement("link") must inherit property "sizes" with the proper type (9)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -10116,102 +10116,6 @@
|
|||
[link.nonce: IDL set to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: typeof IDL attribute]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL get with DOM attribute unset]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to ""]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to 7]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to true]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to false]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to null]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: setAttribute() to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to ""]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to 7]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to true]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to false]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to null]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[link.integrity: IDL set to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[link.referrerPolicy: typeof IDL attribute]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -17610,99 +17610,3 @@
|
|||
[script.nonce: IDL set to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: typeof IDL attribute]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL get with DOM attribute unset]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to ""]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to 7]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to true]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to false]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to null]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: setAttribute() to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to ""]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to 7]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to 1.5]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to true]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to false]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to object "[object Object\]"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to NaN]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to -Infinity]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to "\\0"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to null]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to object "test-toString"]
|
||||
expected: FAIL
|
||||
|
||||
[script.integrity: IDL set to object "test-valueOf"]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
[subresource-integrity.sub.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[Style: <crossorigin='anonymous'> with correct hash, ACAO: *]
|
||||
expected: FAIL
|
||||
|
||||
[Style: Same-origin with correct sha256 and sha512 hash, rel='alternate stylesheet' enabled]
|
||||
expected: NOTRUN
|
||||
|
||||
[Style: Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled]
|
||||
expected: NOTRUN
|
||||
|
||||
[Style: Same-origin with incorrect hash.]
|
||||
expected: FAIL
|
||||
|
||||
[Style: Same-origin with sha256 match, sha512 mismatch]
|
||||
expected: FAIL
|
||||
|
||||
[Style: <crossorigin='use-credentials'> with correct hash, CORS-eligible]
|
||||
expected: FAIL
|
||||
|
||||
[Style: <crossorigin='anonymous'> with CORS-ineligible resource]
|
||||
expected: FAIL
|
||||
|
||||
[Style: Cross-origin, not CORS request, with correct hash]
|
||||
expected: FAIL
|
||||
|
||||
[Style: Cross-origin, not CORS request, with hash mismatch]
|
||||
expected: FAIL
|
||||
|
||||
[Style: <crossorigin='use-credentials'> with incorrect hash CORS-eligible]
|
||||
expected: FAIL
|
||||
|
||||
[Style: <crossorigin='anonymous'> with incorrect hash, ACAO: *]
|
||||
expected: FAIL
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue