Bug 1250254 - Enable ESLint "no-throw-literal" rule for PSM. draft
authorCykesiopka <cykesiopka.bmo@gmail.com>
Mon, 29 Feb 2016 20:05:55 -0800
changeset 335672 95ea086f664a6e2d6c3da25790a669e45da1633e
parent 335671 25b27835c872681a8e97ba1ba27fd5e0acd398d8
child 515191 a048ad01afdb650c1194b2f7b1dd245b92513f2d
push id11847
push usercykesiopka.bmo@gmail.com
push dateTue, 01 Mar 2016 04:06:14 +0000
bugs1250254
milestone47.0a1
Bug 1250254 - Enable ESLint "no-throw-literal" rule for PSM. MozReview-Commit-ID: LZcitO0FTWH
security/manager/.eslintrc
security/manager/.eslintrc.json
security/manager/pki/resources/content/exceptionDialog.js
security/manager/pki/resources/content/viewCertDetails.js
security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_cert_blocklist.js
security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js
security/manager/ssl/tests/unit/test_signed_apps.js
security/manager/tools/dumpGoogleRoots.js
security/manager/tools/genHPKPStaticPins.js
security/manager/tools/genRootCAHashes.js
security/manager/tools/getHSTSPreloadList.js
security/manager/tools/makeCNNICHashes.js
rename from security/manager/.eslintrc
rename to security/manager/.eslintrc.json
--- a/security/manager/.eslintrc
+++ b/security/manager/.eslintrc.json
@@ -123,16 +123,20 @@
     "no-self-compare": 2,
 
     // No declaring variables that hide things like arguments
     "no-shadow-restricted-names": 2,
 
     // No spaces between function name and parentheses
     "no-spaced-func": 2,
 
+    // Disallow throwing literals (eg. |throw "error"| instead of
+    // |throw new Error("error")|)
+    "no-throw-literal": 2,
+
     // No trailing whitespace
     "no-trailing-spaces": 2,
 
     // Error on newline where a semicolon is needed
     "no-unexpected-multiline": 2,
 
     // No unreachable statements
     "no-unreachable": 2,
@@ -169,11 +173,11 @@
 
     // ++ and -- should not need spacing
     "space-unary-ops": [2, { "words": true, "nonwords": false }],
 
     // No comparisons to NaN
     "use-isnan": 2,
 
     // Only check typeof against valid results
-    "valid-typeof": 2,
+    "valid-typeof": 2
   }
 }
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -23,17 +23,17 @@ badCertListener.prototype = {
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
         aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
         aIID.equals(Components.interfaces.nsISupports)) {
       return this;
     }
 
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    throw new Error(Components.results.NS_ERROR_NO_INTERFACE);
   },
   handle_test_result: function () {
     if (gSSLStatus) {
       gCert = gSSLStatus.QueryInterface(Components.interfaces.nsISSLStatus).serverCert;
     }
   },
   notifyCertProblem: function MSR_notifyCertProblem(socketInfo, sslStatus, targetHost) {
     gBroken = true;
--- a/security/manager/pki/resources/content/viewCertDetails.js
+++ b/security/manager/pki/resources/content/viewCertDetails.js
@@ -160,17 +160,17 @@ function listener() {
 
 listener.prototype.QueryInterface =
   function(iid) {
     if (iid.equals(Components.interfaces.nsISupports) ||
         iid.equals(Components.interfaces.nsICertVerificationListener)) {
       return this;
     }
 
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    throw new Error(Components.results.NS_ERROR_NO_INTERFACE);
   };
 
 listener.prototype.notify =
   function(cert, result) {
     DisplayVerificationData(cert, result);
   };
 
 function DisplayVerificationData(cert, result)
--- a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
@@ -16,17 +16,17 @@ FakeSSLStatus.prototype = {
   getInterface: function(aIID) {
     return this.QueryInterface(aIID);
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsISSLStatus) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
     }
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    throw new Error(Cr.NS_ERROR_NO_INTERFACE);
   },
 };
 
 // This is a template to help porting global private browsing tests
 // to per-window private browsing tests
 function test() {
   // initialization
   waitForExplicitFinish();
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -175,17 +175,17 @@ function isSecurityState(expectedState, 
       break;
     case "secure":
       test(ui && !isInsecure && !isBroken && !isEV, "for 'secure' expected flags [0,0,0], " + (message || ""));
       break;
     case "EV":
       test(ui && !isInsecure && !isBroken && isEV, "for 'EV' expected flags [0,0,1], " + (message || ""));
       break;
     default:
-      throw "Invalid isSecurityState state";
+      throw new Error("Invalid isSecurityState state");
   }
 }
 
 function waitForSecurityState(expectedState, callback)
 {
   var roundsLeft = 200; // Wait for 20 seconds (=200*100ms)
   var interval =
   window.setInterval(function() {
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -221,17 +221,17 @@ function clearSessionCache() {
     SSL_ClearSessionCache =
       _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "ssl3");
   } catch (e) {
     // On Windows, this is actually in the nss3 library.
     SSL_ClearSessionCache =
       _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "nss3");
   }
   if (!SSL_ClearSessionCache || SSL_ClearSessionCache() != 0) {
-    throw "Failed to clear SSL session cache";
+    throw new Error("Failed to clear SSL session cache");
   }
 }
 
 // Set up a TLS testing environment that has a TLS server running and
 // ready to accept connections. This async function starts the server and
 // waits for the server to indicate that it is ready.
 //
 // Each test should have its own subdomain of example.com, for example
@@ -626,17 +626,17 @@ FakeSSLStatus.prototype = {
   getInterface: function(aIID) {
     return this.QueryInterface(aIID);
   },
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsISSLStatus) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
     }
-    throw Components.results.NS_ERROR_NO_INTERFACE;
+    throw new Error(Cr.NS_ERROR_NO_INTERFACE);
   },
 };
 
 // Utility functions for adding tests relating to certificate error overrides
 
 // Helper function for add_cert_override_test. Probably doesn't need to be
 // called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
--- a/security/manager/ssl/tests/unit/test_cert_blocklist.js
+++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js
@@ -49,17 +49,17 @@ var appInfo = {
                                          Ci.nsICrashReporter,
                                          Ci.nsISupports])
 };
 
 var XULAppInfoFactory = {
   createInstance: function (outer, iid) {
     appInfo.QueryInterface(iid);
     if (outer != null) {
-      throw Cr.NS_ERROR_NO_AGGREGATION;
+      throw new Error(Cr.NS_ERROR_NO_AGGREGATION);
     }
     return appInfo.QueryInterface(iid);
   }
 };
 
 // we need to ensure we setup revocation data before certDB, or we'll start with
 // no revocation.txt in the profile
 var profile = do_get_profile();
--- a/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js
@@ -22,17 +22,17 @@ function run_test() {
       // Do nothing
     },
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULRuntime])
   };
 
   let xulRuntimeFactory = {
     createInstance: function (outer, iid) {
       if (outer != null) {
-        throw Cr.NS_ERROR_NO_AGGREGATION;
+        throw new Error(Cr.NS_ERROR_NO_AGGREGATION);
       }
       return xulRuntime.QueryInterface(iid);
     }
   };
 
   let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
   const XULRUNTIME_CONTRACTID = "@mozilla.org/xre/runtime;1";
   const XULRUNTIME_CID = Components.ID("{f0f0b230-5525-4127-98dc-7bca39059e70}");
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -69,17 +69,17 @@ function tamper(inFilePath, outFilePath,
     } finally {
       reader.close();
     }
 
     // Any leftover modification means that we were expecting to modify an entry
     // in the input file that wasn't there.
     for (let name in modifications) {
       if (modifications.hasOwnProperty(name)) {
-        throw "input file was missing expected entries: " + name;
+        throw new Error("input file was missing expected entries: " + name);
       }
     }
 
     // Now, append any new entries to the end
     newEntries.forEach(function(newEntry) {
       var sis = Cc["@mozilla.org/io/string-input-stream;1"]
                   .createInstance(Ci.nsIStringInputStream);
       try {
@@ -97,17 +97,18 @@ function tamper(inFilePath, outFilePath,
     writer.close();
   }
 }
 
 function removeEntry(entry, entryInput) { return [null, null]; }
 
 function truncateEntry(entry, entryInput) {
   if (entryInput.available() == 0) {
-    throw "Truncating already-zero length entry will result in identical entry.";
+    throw new Error("Truncating already-zero length entry will result in " +
+                    "identical entry.");
   }
 
   var content = Cc["@mozilla.org/io/string-input-stream;1"]
                   .createInstance(Ci.nsIStringInputStream);
   content.data = "";
 
   return [entry, content];
 }
--- a/security/manager/tools/dumpGoogleRoots.js
+++ b/security/manager/tools/dumpGoogleRoots.js
@@ -20,21 +20,22 @@ var Ci = Components.interfaces;
 function downloadRoots() {
   let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
               .createInstance(Ci.nsIXMLHttpRequest);
   req.open("GET", "https://pki.google.com/roots.pem", false);
   try {
     req.send();
   }
   catch (e) {
-    throw "ERROR: problem downloading Google Root PEMs: " + e;
+    throw new Error("ERROR: problem downloading Google Root PEMs: " + e);
   }
 
   if (req.status != 200) {
-    throw "ERROR: problem downloading Google Root PEMs. Status: " + req.status;
+    throw new Error("ERROR: problem downloading Google Root PEMs. Status: " +
+                    req.status);
   }
 
   let pem = req.responseText;
   let roots = [];
   let currentPEM = "";
   let readingRoot = false;
   let certDB = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -8,20 +8,20 @@
 // 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
 //                                  [path to]/genHPKPStaticpins.js \
 //                                  [absolute path to]/PreloadedHPKPins.json \
 //                                  [an unused argument - see bug 1205406] \
 //                                  [absolute path to]/StaticHPKPins.h
 "use strict";
 
 if (arguments.length != 3) {
-  throw "Usage: genHPKPStaticPins.js " +
-        "<absolute path to PreloadedHPKPins.json> " +
-        "<an unused argument - see bug 1205406> " +
-        "<absolute path to StaticHPKPins.h>";
+  throw new Error("Usage: genHPKPStaticPins.js " +
+                  "<absolute path to PreloadedHPKPins.json> " +
+                  "<an unused argument - see bug 1205406> " +
+                  "<absolute path to StaticHPKPins.h>");
 }
 
 var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
 
 var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 var { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
@@ -117,42 +117,44 @@ function isCertBuiltIn(cert) {
 function download(filename) {
   let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
               .createInstance(Ci.nsIXMLHttpRequest);
   req.open("GET", filename, false); // doing the request synchronously
   try {
     req.send();
   }
   catch (e) {
-    throw "ERROR: problem downloading '" + filename + "': " + e;
+    throw new Error(`ERROR: problem downloading '${filename}': ${e}`);
   }
 
   if (req.status != 200) {
-    throw `ERROR: problem downloading '${filename}': status ${req.status}`;
+    throw new Error("ERROR: problem downloading '" + filename + "': status " +
+                    req.status);
   }
 
   let resultDecoded;
   try {
     resultDecoded = atob(req.responseText);
   }
   catch (e) {
-    throw "ERROR: could not decode data as base64 from '" + filename + "': " + e;
+    throw new Error("ERROR: could not decode data as base64 from '" + filename +
+                    "': " + e);
   }
   return resultDecoded;
 }
 
 function downloadAsJson(filename) {
   // we have to filter out '//' comments, while not mangling the json
   let result = download(filename).replace(/^(\s*)?\/\/[^\n]*\n/mg, "");
   let data = null;
   try {
     data = JSON.parse(result);
   }
   catch (e) {
-    throw "ERROR: could not parse data from '" + filename + "': " + e;
+    throw new Error("ERROR: could not parse data from '" + filename + "': " + e);
   }
   return data;
 }
 
 // Returns a Subject Public Key Digest from the given pem, if it exists.
 function getSKDFromPem(pem) {
   let cert = gCertDB.constructX509FromBase64(pem, pem.length);
   return cert.sha256SubjectPublicKeyInfoDigest;
@@ -166,17 +168,17 @@ function getSKDFromPem(pem) {
  * @returns {String} Base64 encoded SHA-256 hash.
  */
 function sha256Base64(input) {
   let decodedValue;
   try {
     decodedValue = atob(input);
   }
   catch (e) {
-    throw `ERROR: could not decode as base64: '${input}': ${e}`;
+    throw new Error(`ERROR: could not decode as base64: '${input}': ${e}`);
   }
 
   // Convert |decodedValue| to an array so that it can be hashed by the
   // nsICryptoHash instance below.
   // In most cases across the code base, convertToByteArray() of
   // nsIScriptableUnicodeConverter is used to do this, but the method doesn't
   // seem to work here.
   let data = [];
@@ -250,17 +252,18 @@ function downloadAndParseChromeCerts(fil
           certNameToSKD[chromeName] = hash;
           certSKDToName[hash] = chromeName;
           state = PRE_NAME;
         } else if (line.startsWith(BEGIN_CERT)) {
           state = IN_CERT;
         } else if (line.startsWith(BEGIN_PUB_KEY)) {
           state = IN_PUB_KEY;
         } else {
-          throw "ERROR: couldn't parse Chrome certificate file " + line;
+          throw new Error("ERROR: couldn't parse Chrome certificate file " +
+                          "line: " + line);
         }
         break;
       case IN_CERT:
         if (line.startsWith(END_CERT)) {
           state = PRE_NAME;
           hash = getSKDFromPem(pemCert);
           pemCert = "";
           let mozName;
@@ -288,17 +291,17 @@ function downloadAndParseChromeCerts(fil
           chromeNameToHash[chromeName] = hash;
           certNameToSKD[chromeName] = hash;
           certSKDToName[hash] = chromeName;
         } else {
           pemPubKey += line;
         }
         break;
       default:
-        throw "ERROR: couldn't parse Chrome certificate file " + line;
+        throw new Error("ERROR: couldn't parse Chrome certificate file " + line);
     }
   }
   return [ chromeNameToHash, chromeNameToMozName ];
 }
 
 // We can only import pinsets from chrome if for every name in the pinset:
 // - We have a hash from Chrome's static certificate file
 // - We have a builtin cert
@@ -330,17 +333,17 @@ function downloadAndParseChromePins(file
     pin.static_spki_hashes.forEach(function(name) {
       if (name in chromeNameToHash) {
         let hash = chromeNameToHash[name];
         pinset.sha256_hashes.push(certSKDToName[hash]);
 
         // We should have already added hashes for all of these when we
         // imported the certificate file.
         if (!certNameToSKD[name]) {
-          throw "No hash for name: " + name;
+          throw new Error("ERROR: No hash for name: " + name);
         }
       } else if (name in chromeNameToMozName) {
         pinset.sha256_hashes.push(chromeNameToMozName[name]);
       } else {
         dump("Skipping Chrome pinset " + pinset.name + ", couldn't find " +
              "builtin " + name + " from cert file\n");
         valid = false;
       }
@@ -444,29 +447,29 @@ function genExpirationTime() {
   let expirationMicros = expirationMillis * 1000;
   return "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" +
          expirationMicros + ");\n";
 }
 
 function writeFullPinset(certNameToSKD, certSKDToName, pinset) {
   let prefix = "kPinset_" + pinset.name;
   if (!pinset.sha256_hashes || pinset.sha256_hashes.length == 0) {
-    throw `ERROR: Pinset ${pinset.name} does not contain any hashes.`;
+    throw new Error(`ERROR: Pinset ${pinset.name} does not contain any hashes`);
   }
   writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
                     pinset.sha256_hashes);
 }
 
 function writeFingerprints(certNameToSKD, certSKDToName, name, hashes) {
   let varPrefix = "kPinset_" + name;
   writeString("static const char* " + varPrefix + "_Data[] = {\n");
   let SKDList = [];
   for (let certName of hashes) {
     if (!(certName in certNameToSKD)) {
-      throw "Can't find " + certName + " in certNameToSKD";
+      throw new Error(`ERROR: Can't find '${certName}' in certNameToSKD`);
     }
     SKDList.push(certNameToSKD[certName]);
   }
   for (let skd of SKDList.sort()) {
     writeString("  " + nameToAlias(certSKDToName[skd]) + ",\n");
   }
   if (hashes.length == 0) {
     // ANSI C requires that an initialiser list be non-empty.
@@ -498,17 +501,17 @@ function writeEntry(entry) {
   if (entry.is_moz || (entry.pins.indexOf("mozilla") != -1 &&
                        entry.pins != "mozilla_test")) {
     printVal += "true, ";
   } else {
     printVal += "false, ";
   }
   if ("id" in entry) {
     if (entry.id >= 256) {
-      throw "Not enough buckets in histogram";
+      throw new Error("ERROR: Not enough buckets in histogram");
     }
     if (entry.id >= 0) {
       printVal += entry.id + ", ";
     }
   } else {
     printVal += "-1, ";
   }
   printVal += "&kPinset_" + entry.pins;
--- a/security/manager/tools/genRootCAHashes.js
+++ b/security/manager/tools/genRootCAHashes.js
@@ -210,18 +210,19 @@ function insertTrustAnchorsFromDatabase(
     }
   }
 }
 
 //
 //  PRIMARY LOGIC
 //
 
-if (arguments.length < 1) {
-  throw "Usage: genRootCAHashes.js <absolute path to current RootHashes.inc>";
+if (arguments.length != 1) {
+  throw new Error("Usage: genRootCAHashes.js " +
+                  "<absolute path to current RootHashes.inc>");
 }
 
 var trustAnchorsFile = FileUtils.getFile("CurWorkD", [FILENAME_TRUST_ANCHORS]);
 // let rootHashesFile = FileUtils.getFile("CurWorkD", arguments[0]);
 var rootHashesFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
 rootHashesFile.initWithPath(arguments[0]);
 
 // Open the known hashes file; this is to ensure stable bin numbers.
--- a/security/manager/tools/getHSTSPreloadList.js
+++ b/security/manager/tools/getHSTSPreloadList.js
@@ -55,58 +55,61 @@ const POSTFIX =  "};\n";
 function download() {
   var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
             .createInstance(Ci.nsIXMLHttpRequest);
   req.open("GET", SOURCE, false); // doing the request synchronously
   try {
     req.send();
   }
   catch (e) {
-    throw "ERROR: problem downloading '" + SOURCE + "': " + e;
+    throw new Error(`ERROR: problem downloading '${SOURCE}': ${e}`);
   }
 
   if (req.status != 200) {
-    throw "ERROR: problem downloading '" + SOURCE + "': status " + req.status;
+    throw new Error("ERROR: problem downloading '" + SOURCE + "': status " +
+                    req.status);
   }
 
   var resultDecoded;
   try {
     resultDecoded = atob(req.responseText);
   }
   catch (e) {
-    throw "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
   var result = resultDecoded.replace(/^(\s*)?\/\/[^\n]*\n/mg, "");
   var data = null;
   try {
     data = JSON.parse(result);
   }
   catch (e) {
-    throw "ERROR: could not parse data from '" + SOURCE + "': " + e;
+    throw new Error(`ERROR: could not parse data from '${SOURCE}': ${e}`);
   }
   return data;
 }
 
 function getHosts(rawdata) {
   var hosts = [];
 
   if (!rawdata || !rawdata.entries) {
-    throw "ERROR: source data not formatted correctly: 'entries' not found";
+    throw new Error("ERROR: source data not formatted correctly: 'entries' " +
+                    "not found");
   }
 
   for (entry of rawdata.entries) {
     if (entry.mode && entry.mode == "force-https") {
       if (entry.name) {
         entry.retries = MAX_RETRIES;
         entry.originalIncludeSubdomains = entry.include_subdomains;
         hosts.push(entry);
       } else {
-        throw "ERROR: entry not formatted correctly: no name found";
+        throw new Error("ERROR: entry not formatted correctly: no name found");
       }
     }
   }
 
   return hosts;
 }
 
 var gSSService = Cc["@mozilla.org/ssservice;1"]
@@ -152,26 +155,26 @@ function processStsHeader(host, header, 
 }
 
 // RedirectAndAuthStopper prevents redirects and HTTP authentication
 function RedirectAndAuthStopper() {}
 
 RedirectAndAuthStopper.prototype = {
   // nsIChannelEventSink
   asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
-    throw Cr.NS_ERROR_ENTITY_CHANGED;
+    throw new Error(Cr.NS_ERROR_ENTITY_CHANGED);
   },
 
   // nsIAuthPrompt2
   promptAuth: function(channel, level, authInfo) {
     return false;
   },
 
   asyncPromptAuth: function(channel, callback, context, level, authInfo) {
-    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+    throw new Error(Cr.NS_ERROR_NOT_IMPLEMENTED);
   },
 
   getInterface: function(iid) {
     return this.QueryInterface(iid);
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink,
                                          Ci.nsIAuthPrompt2])
@@ -366,18 +369,19 @@ function combineLists(newHosts, currentH
     if (!found) {
       newHosts.push({ name: currentHost, retries: MAX_RETRIES });
     }
   }
 }
 
 // ****************************************************************************
 // This is where the action happens:
-if (arguments.length < 1) {
-  throw "Usage: getHSTSPreloadList.js <absolute path to current nsSTSPreloadList.inc>";
+if (arguments.length != 1) {
+  throw new Error("Usage: getHSTSPreloadList.js " +
+                  "<absolute path to current nsSTSPreloadList.inc>");
 }
 // get the current preload list
 var currentHosts = readCurrentList(arguments[0]);
 // 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
 var rawdata = download();
 // get just the hosts with mode: "force-https"
--- a/security/manager/tools/makeCNNICHashes.js
+++ b/security/manager/tools/makeCNNICHashes.js
@@ -2,17 +2,20 @@
  * 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/. */
 
 // How to run this file:
 // 1. [obtain CNNIC-issued certificates to be whitelisted]
 // 2. [obtain firefox source code]
 // 3. [build/obtain firefox binaries]
 // 4. run `[path to]/run-mozilla.sh [path to]/xpcshell makeCNNICHashes.js \
+//                                  [path to]/intermediatesFile
 //                                  [path to]/certlist'
+//    Where |intermediatesFile| is a file containing PEM encoded intermediate
+//    certificates that the certificates in |certlist| may be issued by.
 //    where certlist is a file containing a list of paths to certificates to
 //    be included in the whitelist
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 
 var gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
@@ -227,17 +230,18 @@ function loadIntermediates(intermediates
   return intermediates;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
 if (arguments.length != 2) {
-  throw "Usage: makeCNNICHashes.js <intermediates file> <path to list of certificates>";
+  throw new Error("Usage: makeCNNICHashes.js <PEM intermediates file> " +
+                  "<path to list of certificates>");
 }
 
 Services.prefs.setIntPref("security.OCSP.enabled", 0);
 var intermediatesFile = pathToFile(arguments[0]);
 var intermediates = loadIntermediates(intermediatesFile);
 var certFile = pathToFile(arguments[1]);
 var { certs, lastValidTime, invalidCerts } = loadCertificates(certFile);