Files
2018-02-02 04:16:08 -05:00

202 lines
6.2 KiB
JavaScript

// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/
"use strict";
// Tests that signed extensions extracted/unpacked into a directory work pass
// verification when non-tampered, and fail verification when tampered via
// various means.
const { ZipUtils } = Cu.import("resource://gre/modules/ZipUtils.jsm", {});
do_get_profile(); // must be called before getting nsIX509CertDB
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
/**
* Signed test extension. This is any arbitrary Mozilla signed XPI that
* preferably has recently been signed (but note that it actually doesn't
* matter, since we ignore expired certificates when checking signing).
* @type nsIFile
*/
var gSignedXPI =
do_get_file("test_signed_dir/lightbeam_for_firefox-1.3.1-fx.xpi", false);
/**
* The directory that the test extension will be extracted to.
* @type nsIFile
*/
var gTarget = FileUtils.getDir("TmpD", ["test_signed_dir"]);
gTarget.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
/**
* Each property below is optional. Defining none of them means "don't tamper".
*
* @typedef {TamperInstructions}
* @type Object
* @property {String[][]} copy
* Format: [[path,newname], [path2,newname2], ...]
* Copy the file located at |path| and name it |newname|.
* @property {String[]} delete
* List of paths to files to delete.
* @property {String[]} corrupt
* List of paths to files to corrupt.
*/
/**
* Extracts the signed XPI into a directory, and tampers the files in that
* directory if instructed.
*
* @param {TamperInstructions} tamper
* Instructions on whether to tamper any files, and if so, how.
* @returns {nsIFile}
* The directory where the XPI was extracted to.
*/
function prepare(tamper) {
ZipUtils.extractFiles(gSignedXPI, gTarget);
// copy files
if (tamper.copy) {
tamper.copy.forEach(i => {
let f = gTarget.clone();
i[0].split("/").forEach(seg => { f.append(seg); });
f.copyTo(null, i[1]);
});
}
// delete files
if (tamper.delete) {
tamper.delete.forEach(i => {
let f = gTarget.clone();
i.split("/").forEach(seg => { f.append(seg); });
f.remove(true);
});
}
// corrupt files
if (tamper.corrupt) {
tamper.corrupt.forEach(i => {
let f = gTarget.clone();
i.split("/").forEach(seg => { f.append(seg); });
let s = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY);
const str = "Kilroy was here";
s.write(str, str.length);
s.close();
});
}
return gTarget;
}
function checkResult(expectedRv, dir, resolve) {
return function verifySignedDirCallback(rv, aSignerCert) {
equal(rv, expectedRv, "Actual and expected return value should match");
equal(aSignerCert != null, Components.isSuccessCode(expectedRv),
"expecting certificate:");
dir.remove(true);
resolve();
};
}
function verifyDirAsync(expectedRv, tamper) {
let targetDir = prepare(tamper);
return new Promise((resolve, reject) => {
certdb.verifySignedDirectoryAsync(
Ci.nsIX509CertDB.AddonsPublicRoot, targetDir,
checkResult(expectedRv, targetDir, resolve));
});
}
//
// the tests
//
add_task(function* testValid() {
yield verifyDirAsync(Cr.NS_OK, {} /* no tampering */);
});
add_task(function* testNoMetaDir() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED,
{delete: ["META-INF"]});
});
add_task(function* testEmptyMetaDir() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED,
{delete: ["META-INF/mozilla.rsa",
"META-INF/mozilla.sf",
"META-INF/manifest.mf"]});
});
add_task(function* testTwoRSAFiles() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{copy: [["META-INF/mozilla.rsa", "extra.rsa"]]});
});
add_task(function* testCorruptRSAFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{corrupt: ["META-INF/mozilla.rsa"]});
});
add_task(function* testMissingSFFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{delete: ["META-INF/mozilla.sf"]});
});
add_task(function* testCorruptSFFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{corrupt: ["META-INF/mozilla.sf"]});
});
add_task(function* testExtraInvalidSFFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
{copy: [["META-INF/mozilla.rsa", "extra.sf"]]});
});
add_task(function* testExtraValidSFFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
{copy: [["META-INF/mozilla.sf", "extra.sf"]]});
});
add_task(function* testMissingManifest() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{delete: ["META-INF/manifest.mf"]});
});
add_task(function* testCorruptManifest() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
{corrupt: ["META-INF/manifest.mf"]});
});
add_task(function* testMissingFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
{delete: ["bootstrap.js"]});
});
add_task(function* testCorruptFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
{corrupt: ["bootstrap.js"]});
});
add_task(function* testExtraFile() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
{copy: [["bootstrap.js", "extra"]]});
});
add_task(function* testMissingFileInDir() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
{delete: ["lib/ui.js"]});
});
add_task(function* testCorruptFileInDir() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
{corrupt: ["lib/ui.js"]});
});
add_task(function* testExtraFileInDir() {
yield verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
{copy: [["lib/ui.js", "extra"]]});
});
do_register_cleanup(function() {
if (gTarget.exists()) {
gTarget.remove(true);
}
});