--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -276,23 +276,29 @@ function* updatePinningList(records) {
// clear the current preload list
siteSecurityService.clearPreloads();
// write each KeyPin entry to the preload list
for (let item of records) {
try {
const {pinType, pins=[], versions} = item;
- if (pinType == "KeyPin" && pins.length &&
- versions.indexOf(appInfo.version) != -1) {
- siteSecurityService.setKeyPins(item.hostName,
- item.includeSubdomains,
- item.expires,
- pins.length,
- pins, true);
+ if (versions.indexOf(appInfo.version) != -1) {
+ if (pinType == "KeyPin" && pins.length) {
+ siteSecurityService.setKeyPins(item.hostName,
+ item.includeSubdomains,
+ item.expires,
+ pins.length,
+ pins, true);
+ }
+ if (pinType == "STSPin") {
+ siteSecurityService.setHSTSPreload(item.hostName,
+ item.includeSubdomains,
+ item.expires);
+ }
}
} catch (e) {
// prevent errors relating to individual preload entries from causing
// sync to fail. We will accumulate telemetry for such failures in bug
// 1254099.
}
}
} else {
--- a/services/common/tests/unit/test_blocklist_pinning.js
+++ b/services/common/tests/unit/test_blocklist_pinning.js
@@ -90,16 +90,17 @@ add_task(function* test_something(){
let sss = Cc["@mozilla.org/ssservice;1"]
.getService(Ci.nsISiteSecurityService);
// ensure our pins are all missing before we start
ok(!sss.isSecureHost(sss.HEADER_HPKP, "one.example.com", 0));
ok(!sss.isSecureHost(sss.HEADER_HPKP, "two.example.com", 0));
ok(!sss.isSecureHost(sss.HEADER_HPKP, "three.example.com", 0));
+ ok(!sss.isSecureHost(sss.HEADER_HSTS, "five.example.com", 0));
// Test an empty db populates
let result = yield PinningPreloadClient.maybeSync(2000, Date.now());
let connection = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
// Open the collection, verify it's been populated:
// Our test data has a single record; it should be in the local collection
@@ -109,20 +110,20 @@ add_task(function* test_something(){
// check that a pin exists for one.example.com
ok(sss.isSecureHost(sss.HEADER_HPKP, "one.example.com", 0));
// Test the db is updated when we call again with a later lastModified value
result = yield PinningPreloadClient.maybeSync(4000, Date.now());
// Open the collection, verify it's been updated:
- // Our data now has three new records; all should be in the local collection
+ // Our data now has four new records; all should be in the local collection
collection = do_get_kinto_collection(connection, COLLECTION_NAME);
list = yield collection.list();
- do_check_eq(list.data.length, 4);
+ do_check_eq(list.data.length, 5);
yield connection.close();
// check that a pin exists for two.example.com and three.example.com
ok(sss.isSecureHost(sss.HEADER_HPKP, "two.example.com", 0));
ok(sss.isSecureHost(sss.HEADER_HPKP, "three.example.com", 0));
// check that a pin does not exist for four.example.com - it's in the
// collection but the version should not match
@@ -139,22 +140,31 @@ add_task(function* test_something(){
// Check the pinning check time pref is modified, even if the collection
// hasn't changed
Services.prefs.setIntPref("services.blocklist.onecrl.checked", 0);
yield PinningPreloadClient.maybeSync(3000, Date.now());
let newValue = Services.prefs.getIntPref("services.blocklist.pinning.checked");
do_check_neq(newValue, 0);
+ // Check that the HSTS preload added to the collection works...
+ ok(sss.isSecureHost(sss.HEADER_HSTS, "five.example.com", 0));
+ // ...and that includeSubdomains is honored
+ ok(!sss.isSecureHost(sss.HEADER_HSTS, "subdomain.five.example.com", 0));
+
// Check that a sync completes even when there's bad data in the
// collection. This will throw on fail, so just calling maybeSync is an
// acceptible test (the data below with last_modified of 300 is nonsense).
Services.prefs.setCharPref("services.settings.server",
`http://localhost:${server.identity.primaryPort}/v1`);
yield PinningPreloadClient.maybeSync(5000, Date.now());
+
+ // The STS entry for five.example.com now has includeSubdomains set;
+ // ensure that the new includeSubdomains value is honored.
+ ok(sss.isSecureHost(sss.HEADER_HSTS, "subdomain.five.example.com", 0));
});
function run_test() {
// Ensure that signature verification is disabled to prevent interference
// with basic certificate sync tests
Services.prefs.setBoolPref("services.blocklist.signing.enforced", false);
// Set up an HTTP Server
@@ -250,16 +260,24 @@ function getSampleResponse(req, port) {
"hostName": "four.example.com",
"includeSubdomains": false,
"expires": new Date().getTime() + 1000000,
"pins" : ["cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
"M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="],
"versions" : ["some version that won't match"],
"id":"dabafde9-df4a-ddba-2548-748da04cc02e",
"last_modified":4000
+ },{
+ "pinType": "STSPin",
+ "hostName": "five.example.com",
+ "includeSubdomains": false,
+ "expires": new Date().getTime() + 1000000,
+ "versions" : [appInfo.version, "some version that won't match"],
+ "id":"dabafde9-df4a-ddba-2548-748da04cc032",
+ "last_modified":4000
}]})
},
"GET:/v1/buckets/pinning/collections/pins/records?_sort=-last_modified&_since=4000": {
"sampleHeaders": [
"Access-Control-Allow-Origin: *",
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
"Content-Type: application/json; charset=UTF-8",
"Server: waitress",
@@ -284,15 +302,23 @@ function getSampleResponse(req, port) {
"irrelevant":"this entry is missing the actual pins",
"pinType": "KeyPin",
"hostName": "missingpins.example.com",
"includeSubdomains": false,
"expires": new Date().getTime() + 1000000,
"versions" : [appInfo.version],
"id":"dabafde9-df4a-ddba-2548-748da04cc031",
"last_modified":5000
+ },{
+ "pinType": "STSPin",
+ "hostName": "five.example.com",
+ "includeSubdomains": true,
+ "expires": new Date().getTime() + 1000000,
+ "versions" : [appInfo.version, "some version that won't match"],
+ "id":"dabafde9-df4a-ddba-2548-748da04cc032",
+ "last_modified":5000
}]})
}
};
return responses[`${req.method}:${req.path}?${req.queryString}`] ||
responses[req.method];
}