copy from security/manager/tools/getHSTSPreloadList.js
copy to taskcluster/docker/periodic_updates/scripts/getHSTSPreloadList.js
--- a/security/manager/tools/getHSTSPreloadList.js
+++ b/taskcluster/docker/periodic_updates/scripts/getHSTSPreloadList.js
@@ -1,74 +1,75 @@
-/* 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/. */
+/* 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 strict";
// How to run this file:
// 1. [obtain firefox source code]
// 2. [build/obtain firefox binaries]
-// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
-// [path to]/getHSTSPreloadlist.js \
-// [absolute path to]/nsSTSPreloadlist.inc'
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell [path to]/getHSTSPreloadlist.js [absolute path to]/nsSTSPreloadlist.inc'
// Note: Running this file outputs a new nsSTSPreloadlist.inc in the current
// working directory.
-var gSSService = Cc["@mozilla.org/ssservice;1"]
- .getService(Ci.nsISiteSecurityService);
+/*
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+*/
+var gSSService = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["XMLHttpRequest"]);
const SOURCE = "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT";
const OUTPUT = "nsSTSPreloadList.inc";
const ERROR_OUTPUT = "nsSTSPreloadList.errors";
const MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 18;
-const MAX_CONCURRENT_REQUESTS = 5;
-const MAX_RETRIES = 3;
+const MAX_CONCURRENT_REQUESTS = 500;
+const MAX_RETRIES = 1;
const REQUEST_TIMEOUT = 30 * 1000;
const ERROR_NONE = "no error";
const ERROR_CONNECTING_TO_HOST = "could not connect to host";
const ERROR_NO_HSTS_HEADER = "did not receive HSTS header";
const ERROR_MAX_AGE_TOO_LOW = "max-age too low: ";
-const HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
-" * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
-" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
-"\n" +
-"/*****************************************************************************/\n" +
-"/* This is an automatically generated file. If you're not */\n" +
-"/* nsSiteSecurityService.cpp, you shouldn't be #including it. */\n" +
-"/*****************************************************************************/\n" +
-"\n" +
-"#include <stdint.h>\n";
+const HEADER = `/* 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/. */
+
+/*****************************************************************************/
+/* This is an automatically generated file. If you're not */
+/* nsSiteSecurityService.cpp, you shouldn't be #including it. */
+/*****************************************************************************/
+
+#include <stdint.h>
+`;
+
const GPERF_DELIM = "%%\n";
function download() {
let req = new XMLHttpRequest();
req.open("GET", SOURCE, false); // doing the request synchronously
try {
req.send();
} catch (e) {
throw new Error(`ERROR: problem downloading '${SOURCE}': ${e}`);
}
if (req.status != 200) {
- throw new Error("ERROR: problem downloading '" + SOURCE + "': status " +
- req.status);
+ throw new Error("ERROR: problem downloading '" + SOURCE + "': status " + req.status);
}
let resultDecoded;
try {
resultDecoded = atob(req.responseText);
} catch (e) {
- throw new Error("ERROR: could not decode data as base64 from '" + SOURCE +
- "': " + e);
+ throw new Error("ERROR: could not decode data as base64 from '" + SOURCE + "': " + e);
}
// we have to filter out '//' comments, while not mangling the json
let result = resultDecoded.replace(/^(\s*)?\/\/[^\n]*\n/mg, "");
let data = null;
try {
data = JSON.parse(result);
} catch (e) {
@@ -76,18 +77,17 @@ function download() {
}
return data;
}
function getHosts(rawdata) {
let hosts = [];
if (!rawdata || !rawdata.entries) {
- throw new Error("ERROR: source data not formatted correctly: 'entries' " +
- "not found");
+ throw new Error("ERROR: source data not formatted correctly: 'entries' not found");
}
for (let entry of rawdata.entries) {
if (entry.mode && entry.mode == "force-https") {
if (entry.name) {
// We trim the entry name here to avoid malformed URI exceptions when we
// later try to connect to the domain.
entry.name = entry.name.trim();
@@ -100,49 +100,50 @@ function getHosts(rawdata) {
}
}
}
return hosts;
}
function processStsHeader(host, header, status, securityInfo) {
- let maxAge = { value: 0 };
- let includeSubdomains = { value: false };
+ let maxAge = {
+ value: 0
+ };
+ let includeSubdomains = {
+ value: false
+ };
let error = ERROR_NONE;
if (header != null && securityInfo != null) {
try {
let uri = Services.io.newURI("https://" + host.name);
- let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
- .SSLStatus;
- gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS,
- uri, header, sslStatus, 0,
- Ci.nsISiteSecurityService.SOURCE_PRELOAD_LIST,
- {}, maxAge, includeSubdomains);
+ let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+ gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, header, sslStatus, 0, Ci.nsISiteSecurityService.SOURCE_PRELOAD_LIST, {}, maxAge, includeSubdomains);
} catch (e) {
- dump("ERROR: could not process header '" + header + "' from " +
- host.name + ": " + e + "\n");
+ dump("ERROR: could not process header '" + header + "' from " + host.name + ": " + e + "\n");
error = e;
}
} else if (status == 0) {
error = ERROR_CONNECTING_TO_HOST;
} else {
error = ERROR_NO_HSTS_HEADER;
}
if (error == ERROR_NONE && maxAge.value < MINIMUM_REQUIRED_MAX_AGE) {
error = ERROR_MAX_AGE_TOO_LOW;
}
- return { name: host.name,
- maxAge: maxAge.value,
- includeSubdomains: includeSubdomains.value,
- error,
- retries: host.retries - 1,
- forceInclude: host.forceInclude };
+ return {
+ name: host.name,
+ maxAge: maxAge.value,
+ includeSubdomains: includeSubdomains.value,
+ error,
+ retries: host.retries - 1,
+ forceInclude: host.forceInclude
+ };
}
// RedirectAndAuthStopper prevents redirects and HTTP authentication
function RedirectAndAuthStopper() {}
RedirectAndAuthStopper.prototype = {
// nsIChannelEventSink
asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
@@ -157,56 +158,46 @@ RedirectAndAuthStopper.prototype = {
asyncPromptAuth(channel, callback, context, level, authInfo) {
throw new Error(Cr.NS_ERROR_NOT_IMPLEMENTED);
},
getInterface(iid) {
return this.QueryInterface(iid);
},
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink,
- Ci.nsIAuthPrompt2])
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink, Ci.nsIAuthPrompt2])
};
-function getHSTSStatus(host, resultList) {
- let req = new XMLHttpRequest();
- let inResultList = false;
+function fetchstatus(host) {
+ let xhr = new XMLHttpRequest();
let uri = "https://" + host.name + "/";
- req.open("GET", uri, true);
- req.setRequestHeader("X-Automated-Tool",
- "https://hg.mozilla.org/mozilla-central/file/tip/security/manager/tools/getHSTSPreloadList.js");
- req.timeout = REQUEST_TIMEOUT;
- let errorhandler = (evt) => {
- dump(`ERROR: error making request to ${host.name} (type=${evt.type})\n`);
- if (!inResultList) {
- inResultList = true;
- resultList.push(processStsHeader(host, null, req.status,
- req.channel.securityInfo));
- }
- };
- req.onerror = errorhandler;
- req.ontimeout = errorhandler;
- req.onabort = errorhandler;
-
- req.onload = function(event) {
- if (!inResultList) {
- inResultList = true;
- var header = req.getResponseHeader("strict-transport-security");
- resultList.push(processStsHeader(host, header, req.status,
- req.channel.securityInfo));
- }
- };
+ xhr.open("head", uri, true);
+ xhr.setRequestHeader("X-Automated-Tool", "https://hg.mozilla.org/mozilla-central/file/tip/security/manager/tools/getHSTSPreloadList.js");
+ xhr.timeout = REQUEST_TIMEOUT;
try {
- req.channel.notificationCallbacks = new RedirectAndAuthStopper();
- req.send();
+ xhr.channel.notificationCallbacks = new RedirectAndAuthStopper();
+ xhr.send();
} catch (e) {
dump("ERROR: exception making request to " + host.name + ": " + e + "\n");
+ return processStsHeader(host, null, xhr.status, xhr.channel.securityInfo);
}
+
+ let header = xhr.getResponseHeader("strict-transport-security");
+ return processStsHeader(host, header, xhr.status, xhr.channel.securityInfo);
+}
+
+async function getHSTSStatus(host) {
+ return new Promise((resolve, reject) => {
+ do {
+ host = fetchstatus(host);
+ } while (shouldRetry(host));
+ resolve(host);
+ });
}
function compareHSTSStatus(a, b) {
if (a.name > b.name) {
return 1;
}
if (a.name < b.name) {
return -1;
@@ -225,146 +216,88 @@ function getExpirationTimeString() {
let now = new Date();
let nowMillis = now.getTime();
// MINIMUM_REQUIRED_MAX_AGE is in seconds, so convert to milliseconds
let expirationMillis = nowMillis + (MINIMUM_REQUIRED_MAX_AGE * 1000);
let expirationMicros = expirationMillis * 1000;
return "const PRTime gPreloadListExpirationTime = INT64_C(" + expirationMicros + ");\n";
}
-function errorToString(status) {
- return (status.error == ERROR_MAX_AGE_TOO_LOW
- ? status.error + status.maxAge
- : status.error);
+function shouldRetry(response) {
+ return (response.error != ERROR_NO_HSTS_HEADER && response.error != ERROR_MAX_AGE_TOO_LOW && response.error != ERROR_NONE && response.retries > 0);
}
-function output(sortedStatuses, currentList) {
- try {
- let file = FileUtils.getFile("CurWorkD", [OUTPUT]);
- let errorFile = FileUtils.getFile("CurWorkD", [ERROR_OUTPUT]);
- let fos = FileUtils.openSafeFileOutputStream(file);
- let eos = FileUtils.openSafeFileOutputStream(errorFile);
- writeTo(HEADER, fos);
- writeTo(getExpirationTimeString(), fos);
-
- for (let status of sortedStatuses) {
- // If we've encountered an error for this entry (other than the site not
- // sending an HSTS header), be safe and don't remove it from the list
- // (given that it was already on the list).
- if (!status.forceInclude &&
- status.error != ERROR_NONE &&
- status.error != ERROR_NO_HSTS_HEADER &&
- status.error != ERROR_MAX_AGE_TOO_LOW &&
- status.name in currentList) {
- dump("INFO: error connecting to or processing " + status.name + " - using previous status on list\n");
- writeTo(status.name + ": " + errorToString(status) + "\n", eos);
- status.maxAge = MINIMUM_REQUIRED_MAX_AGE;
- status.includeSubdomains = currentList[status.name];
- }
- }
- // Filter out entries we aren't including.
- var includedStatuses = sortedStatuses.filter(function (status) {
- if (status.maxAge < MINIMUM_REQUIRED_MAX_AGE && !status.forceInclude) {
- dump("INFO: " + status.name + " NOT ON the preload list\n");
- writeTo(status.name + ": " + errorToString(status) + "\n", eos);
- return false;
- }
+// Copied from browser/components/migration/MigrationUtils.jsm
+function spinResolve(promise) {
+ if (!(promise instanceof Promise)) {
+ return promise;
+ }
+ let done = false;
+ let result = null;
+ let error = null;
+ promise.catch(e => {
+ error = e;
+ }).then(r => {
+ result = r;
+ done = true;
+ });
- dump("INFO: " + status.name + " ON the preload list (includeSubdomains: "
- + status.includeSubdomains + "\n");
- if (status.forceInclude && status.error != ERROR_NONE) {
- writeTo(status.name + ": " + errorToString(status) + " (error "
- + "ignored - included regardless)\n", eos);
- }
- return true;
- });
-
- writeTo(GPERF_DELIM, fos);
-
- for (let status of includedStatuses) {
- let includeSubdomains = (status.includeSubdomains ? 1 : 0);
- writeTo(status.name + ", " + includeSubdomains + "\n", fos);
- }
-
- writeTo(GPERF_DELIM, fos);
- FileUtils.closeSafeFileOutputStream(fos);
- FileUtils.closeSafeFileOutputStream(eos);
- } catch (e) {
- dump("ERROR: problem writing output to '" + OUTPUT + "': " + e + "\n");
+ Services.tm.spinEventLoopUntil(() => done);
+ if (error) {
+ throw error;
+ } else {
+ return result;
}
}
-function shouldRetry(response) {
- return (response.error != ERROR_NO_HSTS_HEADER &&
- response.error != ERROR_MAX_AGE_TOO_LOW &&
- response.error != ERROR_NONE && response.retries > 0);
-}
+async function probeHSTSStatuses(inHosts) {
+ let promises = [];
+
+ dump("Examining " + inHosts.length + " hosts.\n");
-function probeHSTSStatuses(inHosts) {
- let outStatuses = [];
- let expectedOutputLength = inHosts.length;
- let tmpOutput = [];
- for (let i = 0; i < MAX_CONCURRENT_REQUESTS && inHosts.length > 0; i++) {
+ // Debug/testing on a small number of hosts
+ // while (inHosts.length > 40000) {
+
+ while (inHosts.length > 0) {
let host = inHosts.shift();
- dump("spinning off request to '" + host.name + "' (remaining retries: " +
- host.retries + ")\n");
- getHSTSStatus(host, tmpOutput);
+ promises.push(getHSTSStatus(host));
}
- while (outStatuses.length != expectedOutputLength) {
- waitForAResponse(tmpOutput);
- let response = tmpOutput.shift();
- dump("request to '" + response.name + "' finished\n");
- if (shouldRetry(response)) {
- inHosts.push(response);
- } else {
- outStatuses.push(response);
- }
+ dump("Waiting for " + promises.length + " responses.\n");
- if (inHosts.length > 0) {
- let host = inHosts.shift();
- dump("spinning off request to '" + host.name + "' (remaining retries: " +
- host.retries + ")\n");
- getHSTSStatus(host, tmpOutput);
- }
- }
-
- return outStatuses;
-}
-
-// Since all events are processed on the main thread, and since event
-// handlers are not preemptible, there shouldn't be any concurrency issues.
-function waitForAResponse(outputList) {
- // From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
- Services.tm.spinEventLoopUntil(() => outputList.length != 0);
+ let result = await Promise.all(promises);
+ dump("HSTS Probe received " + result.length + " statuses.\n");
+ return result;
}
function readCurrentList(filename) {
var currentHosts = {};
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(filename);
- var fis = Cc["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Ci.nsILineInputStream);
+ var fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsILineInputStream);
fis.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
var line = {};
// While we generate entries matching the latest version format,
// we still need to be able to read entries in the previous version formats
// for bootstrapping a latest version preload list from a previous version
// preload list. Hence these regexes.
const entryRegexes = [
- /([^,]+), (0|1)/, // v3
- / {2}\/\* "([^"]*)", (true|false) \*\//, // v2
- / {2}{ "([^"]*)", (true|false) },/, // v1
+ /([^,]+), (0|1)/, // v3
+ / {2}\/\* "([^"]*)", (true|false) \*\//, // v2
+ / {2}{ "([^"]*)", (true|false) },/, // v1
];
while (fis.readLine(line)) {
let match;
- entryRegexes.find((r) => { match = r.exec(line.value); return match; });
+ entryRegexes.find((r) => {
+ match = r.exec(line.value);
+ return match;
+ });
if (match) {
currentHosts[match[1]] = (match[2] == "1" || match[2] == "true");
}
}
return currentHosts;
}
function combineLists(newHosts, currentHosts) {
@@ -372,101 +305,174 @@ function combineLists(newHosts, currentH
let found = false;
for (let newHost of newHosts) {
if (newHost.name == currentHost) {
found = true;
break;
}
}
if (!found) {
- newHosts.push({ name: currentHost, retries: MAX_RETRIES });
+ newHosts.push({name: currentHost, retries: MAX_RETRIES});
}
}
}
const TEST_ENTRIES = [
- { name: "includesubdomains.preloaded.test", includeSubdomains: true },
- { name: "includesubdomains2.preloaded.test", includeSubdomains: true },
- { name: "noincludesubdomains.preloaded.test", includeSubdomains: false },
+ {
+ name: "includesubdomains.preloaded.test",
+ includeSubdomains: true
+ }, {
+ name: "includesubdomains2.preloaded.test",
+ includeSubdomains: true
+ }, {
+ name: "noincludesubdomains.preloaded.test",
+ includeSubdomains: false
+ }
];
function deleteTestHosts(currentHosts) {
for (let testEntry of TEST_ENTRIES) {
delete currentHosts[testEntry.name];
}
}
function getTestHosts() {
let hosts = [];
for (let testEntry of TEST_ENTRIES) {
hosts.push({
- name: testEntry.name,
- maxAge: MINIMUM_REQUIRED_MAX_AGE,
- includeSubdomains: testEntry.includeSubdomains,
- error: ERROR_NONE,
+ name: testEntry.name, maxAge: MINIMUM_REQUIRED_MAX_AGE, includeSubdomains: testEntry.includeSubdomains, error: ERROR_NONE,
// This deliberately doesn't have a value for `retries` (because we should
// never attempt to connect to this host).
- forceInclude: true,
+ forceInclude: true
});
}
return hosts;
}
-function insertHosts(inoutHostList, inAddedHosts) {
+async function insertHosts(inoutHostList, inAddedHosts) {
for (let host of inAddedHosts) {
inoutHostList.push(host);
}
}
function filterForcedInclusions(inHosts, outNotForced, outForced) {
// Apply our filters (based on policy today) to determine which entries
// will be included without being checked (forced); the others will be
// checked using active probing.
for (let host of inHosts) {
- if (host.policy == "google" || host.policy == "public-suffix" ||
- host.policy == "public-suffix-requested") {
+ if (host.policy == "google" || host.policy == "public-suffix" || host.policy == "public-suffix-requested") {
host.forceInclude = true;
host.error = ERROR_NONE;
outForced.push(host);
} else {
outNotForced.push(host);
}
}
}
-// ****************************************************************************
-// This is where the action happens:
-if (arguments.length != 1) {
- throw new Error("Usage: getHSTSPreloadList.js " +
- "<absolute path to current nsSTSPreloadList.inc>");
+function output(statuses) {
+ dump("INFO: Writing output to " + OUTPUT + "\n");
+ try {
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+
+ let file = FileUtils.getFile("CurWorkD", [OUTPUT]);
+ let fos = FileUtils.openSafeFileOutputStream(file);
+ writeTo(HEADER, fos);
+ writeTo(getExpirationTimeString(), fos);
+
+ writeTo(GPERF_DELIM, fos);
+
+ for (let status of statuses) {
+ let includeSubdomains = (
+ status.includeSubdomains
+ ? 1
+ : 0);
+ writeTo(status.name + ", " + includeSubdomains + "\n", fos);
+ }
+
+ writeTo(GPERF_DELIM, fos);
+ FileUtils.closeSafeFileOutputStream(fos);
+ dump("finished writing output file\n");
+ } catch (e) {
+ dump("ERROR: problem writing output to '" + OUTPUT + "': " + e + "\n");
+ }
+}
+
+function errorToString(status) {
+ return (
+ status.error == ERROR_MAX_AGE_TOO_LOW
+ ? status.error + status.maxAge
+ : status.error);
}
-// get the current preload list
-let currentHosts = readCurrentList(arguments[0]);
-// delete any hosts we use in tests so we don't actually connect to them
-deleteTestHosts(currentHosts);
-// disable the current preload list so it won't interfere with requests we make
-Services.prefs.setBoolPref("network.stricttransportsecurity.preloadlist", false);
-// download and parse the raw json file from the Chromium source
-let rawdata = download();
-// get just the hosts with mode: "force-https"
-let hosts = getHosts(rawdata);
-// add hosts in the current list to the new list (avoiding duplicates)
-combineLists(hosts, currentHosts);
-// Don't contact hosts that are forced to be included anyway
-let hostsToContact = [];
-let forcedHosts = [];
-filterForcedInclusions(hosts, hostsToContact, forcedHosts);
-// Initialize the final status list
-let hstsStatuses = [];
-// Add the hosts we use in tests
-insertHosts(hstsStatuses, getTestHosts());
-// Add in the hosts that are forced
-insertHosts(hstsStatuses, forcedHosts);
-// Probe the HSTS status of each host and add them into hstsStatuses
-let probedStatuses = probeHSTSStatuses(hostsToContact);
-insertHosts(hstsStatuses, probedStatuses);
-// Sort the hosts alphabetically
-hstsStatuses.sort(compareHSTSStatus);
-// Write the results to a file (this is where we filter out hosts that we
-// either couldn't connect to, didn't receive an HSTS header from, couldn't
-// parse the header, or had a header with too short a max-age)
-output(hstsStatuses, currentHosts);
-// ****************************************************************************
+
+async function main(args) {
+ if (args.length != 1) {
+ throw new Error("Usage: getHSTSPreloadList.js <absolute path to current nsSTSPreloadList.inc>");
+ }
+
+ // get the current preload list
+ let currentHosts = readCurrentList(args[0]);
+ // delete any hosts we use in tests so we don't actually connect to them
+ deleteTestHosts(currentHosts);
+ // disable the current preload list so it won't interfere with requests we make
+ Services.prefs.setBoolPref("network.stricttransportsecurity.preloadlist", false);
+ // download and parse the raw json file from the Chromium source
+ let rawdata = download();
+ // get just the hosts with mode: "force-https"
+ let hosts = getHosts(rawdata);
+ // add hosts in the current list to the new list (avoiding duplicates)
+ combineLists(hosts, currentHosts);
+
+ // Don't contact hosts that are forced to be included anyway
+ let hostsToContact = [];
+ let forcedHosts = [];
+ filterForcedInclusions(hosts, hostsToContact, forcedHosts);
+
+ // Initialize the final status list
+ let hstsStatuses = [];
+ // Add the hosts we use in tests
+ dump("Adding test hosts\n");
+ insertHosts(hstsStatuses, getTestHosts());
+ // Add in the hosts that are forced
+ dump("Adding forced hosts\n");
+ insertHosts(hstsStatuses, forcedHosts);
+
+ let total = await probeHSTSStatuses(hostsToContact).then(function(probedStatuses) {
+ return hstsStatuses.concat(probedStatuses);
+ }).then(function(statuses) {
+ return statuses.sort(compareHSTSStatus);
+ }).then(function(statuses) {
+ for (let status of statuses) {
+ // If we've encountered an error for this entry (other than the site not
+ // sending an HSTS header), be safe and don't remove it from the list
+ // (given that it was already on the list).
+ if (!status.forceInclude && status.error != ERROR_NONE && status.error != ERROR_NO_HSTS_HEADER && status.error != ERROR_MAX_AGE_TOO_LOW && status.name in currentHosts) {
+ // dump("INFO: error connecting to or processing " + status.name + " - using previous status on list\n");
+ status.maxAge = MINIMUM_REQUIRED_MAX_AGE;
+ status.includeSubdomains = currentHosts[status.name];
+ }
+ }
+ return statuses;
+ }).then(function(statuses) {
+ // Filter out entries we aren't including.
+ var includedStatuses = statuses.filter(function(status) {
+ if (status.maxAge < MINIMUM_REQUIRED_MAX_AGE && !status.forceInclude) {
+ // dump("INFO: " + status.name + " NOT ON the preload list\n");
+ return false;
+ }
+
+ // dump("INFO: " + status.name + " ON the preload list (includeSubdomains: " + status.includeSubdomains + ")\n");
+ if (status.forceInclude && status.error != ERROR_NONE) {
+ dump(status.name + ": " + errorToString(status) + " (error ignored - included regardless)\n");
+ }
+ return true;
+ });
+ return includedStatuses;
+ });
+
+ // Write the output file
+ output(total);
+
+ dump("HSTS probing all done\n");
+}
+
+// arguments is a global within xpcshell
+spinResolve(main(arguments));