mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Implement mutable HSTS list
This prepares the resource task to update the HSTS list when it sees STS headers. This will allow full HSTS support for servo/servo#6105 when the resource task implements the header checking
This commit is contained in:
parent
aa19a9a741
commit
d2f35555b9
2 changed files with 127 additions and 14 deletions
|
@ -13,11 +13,12 @@ use cookie;
|
|||
use mime_classifier::MIMEClassifier;
|
||||
|
||||
use net_traits::{ControlMsg, LoadData, LoadResponse, LoadConsumer};
|
||||
use net_traits::{Metadata, ProgressMsg, ResourceTask, AsyncResponseTarget, ResponseAction};
|
||||
use net_traits::{Metadata, ProgressMsg, ResourceTask, AsyncResponseTarget, ResponseAction, CookieSource};
|
||||
use net_traits::ProgressMsg::Done;
|
||||
use util::opts;
|
||||
use util::task::spawn_named;
|
||||
use util::resource_files::read_resource_file;
|
||||
use url::Url;
|
||||
|
||||
use devtools_traits::{DevtoolsControlMsg};
|
||||
use hyper::header::{ContentType, Header, SetCookie, UserAgent};
|
||||
|
@ -188,6 +189,16 @@ pub struct HSTSEntry {
|
|||
pub include_subdomains: bool
|
||||
}
|
||||
|
||||
impl HSTSEntry {
|
||||
fn matches_domain(&self, host: &str) -> bool {
|
||||
self.host == host
|
||||
}
|
||||
|
||||
fn matches_subdomain(&self, host: &str) -> bool {
|
||||
host.ends_with(&format!(".{}", self.host))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct HSTSList {
|
||||
pub entries: Vec<HSTSEntry>
|
||||
|
@ -204,15 +215,56 @@ impl HSTSList {
|
|||
pub fn always_secure(&self, host: &str) -> bool {
|
||||
// TODO - Should this be faster than O(n)? The HSTS list is only a few
|
||||
// hundred or maybe thousand entries...
|
||||
//
|
||||
// Could optimise by searching for exact matches first (via a map or
|
||||
// something), then checking for subdomains.
|
||||
self.entries.iter().any(|e| {
|
||||
if e.include_subdomains {
|
||||
host.ends_with(&format!(".{}", e.host)) || e.host == host
|
||||
e.matches_subdomain(host) || e.matches_domain(host)
|
||||
} else {
|
||||
e.host == host
|
||||
e.matches_domain(host)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn has_domain(&self, host: String) -> bool {
|
||||
self.entries.iter().any(|e| {
|
||||
e.matches_domain(&host)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_subdomain(&self, host: String) -> bool {
|
||||
self.entries.iter().any(|e| {
|
||||
e.matches_subdomain(&host)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push(&mut self, host: String, include_subdomains: bool) {
|
||||
let have_domain = self.has_domain(host.clone());
|
||||
let have_subdomain = self.has_subdomain(host.clone());
|
||||
|
||||
if !have_domain && !have_subdomain {
|
||||
self.entries.push(HSTSEntry {
|
||||
host: host,
|
||||
include_subdomains: include_subdomains
|
||||
});
|
||||
} else if !have_subdomain {
|
||||
self.entries = self.entries.iter().fold(Vec::new(), |mut m, e| {
|
||||
let new = HSTSEntry {
|
||||
host: host.clone(),
|
||||
include_subdomains: include_subdomains
|
||||
};
|
||||
|
||||
if e.matches_domain(&host) {
|
||||
m.push(new);
|
||||
} else {
|
||||
m.push(new);
|
||||
}
|
||||
|
||||
m
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_hsts_secure(&self, load_data: LoadData) -> LoadData {
|
||||
if let Some(h) = load_data.url.domain() {
|
||||
|
@ -300,13 +352,7 @@ impl ResourceManager {
|
|||
|
||||
|
||||
impl ResourceManager {
|
||||
fn start(&mut self) {
|
||||
loop {
|
||||
match self.from_client.recv().unwrap() {
|
||||
ControlMsg::Load(load_data, consumer) => {
|
||||
self.load(load_data, consumer)
|
||||
}
|
||||
ControlMsg::SetCookiesForUrl(request, cookie_list, source) => {
|
||||
fn set_cookies_for_url(&mut self, request: Url, cookie_list: String, source: CookieSource) {
|
||||
let header = Header::parse_header(&[cookie_list.into_bytes()]);
|
||||
if let Ok(SetCookie(cookies)) = header {
|
||||
for bare_cookie in cookies.into_iter() {
|
||||
|
@ -316,6 +362,16 @@ impl ResourceManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&mut self) {
|
||||
loop {
|
||||
match self.from_client.recv().unwrap() {
|
||||
ControlMsg::Load(load_data, consumer) => {
|
||||
self.load(load_data, consumer)
|
||||
}
|
||||
ControlMsg::SetCookiesForUrl(request, cookie_list, source) => {
|
||||
self.set_cookies_for_url(request, cookie_list, source)
|
||||
}
|
||||
ControlMsg::GetCookiesForUrl(url, consumer, source) => {
|
||||
consumer.send(self.cookie_storage.cookies_for_url(&url, source)).unwrap();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,63 @@ fn test_exit() {
|
|||
resource_task.send(ControlMsg::Exit).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
|
||||
let mut list = HSTSList {
|
||||
entries: vec!(HSTSEntry {
|
||||
host: "mozilla.org".to_string(),
|
||||
include_subdomains: true
|
||||
})
|
||||
};
|
||||
|
||||
list.push("servo.mozilla.org".to_string(), false);
|
||||
|
||||
assert!(list.entries.len() == 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
|
||||
let mut list = HSTSList {
|
||||
entries: vec!(HSTSEntry {
|
||||
host: "mozilla.org".to_string(),
|
||||
include_subdomains: true
|
||||
})
|
||||
};
|
||||
|
||||
assert!(list.always_secure("servo.mozilla.org"));
|
||||
|
||||
list.push("mozilla.org".to_string(), false);
|
||||
|
||||
assert!(!list.always_secure("servo.mozilla.org"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
|
||||
let mut list = HSTSList {
|
||||
entries: vec!(HSTSEntry {
|
||||
host: "mozilla.org".to_string(),
|
||||
include_subdomains: false
|
||||
})
|
||||
};
|
||||
|
||||
list.push("mozilla.org".to_string(), false);
|
||||
|
||||
assert!(list.entries.len() == 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_entry_to_hsts_list_should_add_an_entry() {
|
||||
let mut list = HSTSList {
|
||||
entries: Vec::new()
|
||||
};
|
||||
|
||||
assert!(!list.always_secure("mozilla.org"));
|
||||
|
||||
list.push("mozilla.org".to_string(), true);
|
||||
|
||||
assert!(list.always_secure("mozilla.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
|
||||
let mock_preload_content = "derp";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue