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
*Wait for response body
*If the response does not have a termination reason and response does not
match request’s integrity metadata, set response to a
network error.# Please enter the commit message for your changes. Lines starting
This commit is contained in:
mrnayak 2017-01-08 08:52:18 +05:30
parent 496447a363
commit a3026499f4
19 changed files with 439 additions and 260 deletions

View file

@ -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);
}

View file

@ -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};

View 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));
}