зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1226928 - content-signature verification tests for about:newtab, r=mconley
This commit is contained in:
Родитель
ad50543437
Коммит
2b22d469bb
|
@ -0,0 +1,10 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_contentserver.sjs
|
||||
file_about_newtab.html
|
||||
file_about_newtab_bad.html
|
||||
file_about_newtab_good_signature
|
||||
file_about_newtab_bad_signature
|
||||
file_about_newtab_broken_signature
|
||||
|
||||
[browser_verify_content_about_newtab.js]
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Test Content-Signature for remote about:newtab
|
||||
* - Bug 1226928 - allow about:newtab to load remote content
|
||||
*
|
||||
* This tests content-signature verification on remote about:newtab in the
|
||||
* following cases (see TESTS, all failed loads display about:blank fallback):
|
||||
* - good case (signature should verify and correct page is displayed)
|
||||
* - reload of newtab when the siganture was invalidated after the last correct
|
||||
* load
|
||||
* - malformed content-signature header
|
||||
* - malformed keyid directive
|
||||
* - malformed p384ecdsa directive
|
||||
* - wrong signature (this is not a siganture for the delivered document)
|
||||
* - invalid signature (this is not even a signature)
|
||||
* - loading a file that doesn't fit the key or signature
|
||||
* - cache poisoning (load a malicious remote page not in newtab, subsequent
|
||||
* newtab load has to load the fallback)
|
||||
*/
|
||||
|
||||
const ABOUT_NEWTAB_URI = "about:newtab";
|
||||
|
||||
const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
|
||||
const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good";
|
||||
|
||||
const INVALIDATE_FILE = BASE + "invalidateFile=yep";
|
||||
const VALIDATE_FILE = BASE + "validateFile=yep";
|
||||
|
||||
const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header=";
|
||||
const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
|
||||
const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid";
|
||||
const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
|
||||
const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
|
||||
|
||||
const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good";
|
||||
const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good";
|
||||
const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good";
|
||||
const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good";
|
||||
const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad";
|
||||
|
||||
const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true";
|
||||
|
||||
const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
|
||||
const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
|
||||
const ABOUT_BLANK = "<head></head><body></body>";
|
||||
|
||||
const TESTS = [
|
||||
// { newtab (aboutURI) or regular load (url) : url,
|
||||
// testString : expected string in the loaded page }
|
||||
{ "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING },
|
||||
{ "aboutURI" : URI_ERROR_HEADER, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_KEYERROR_HEADER, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_SIGERROR_HEADER, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_NO_HEADER, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_BAD_SIG, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_BROKEN_SIG, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_BAD_KEY, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_BAD_FILE, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_BAD_ALL, "testString" : ABOUT_BLANK },
|
||||
{ "url" : URI_BAD_FILE_CACHED, "testString" : BAD_ABOUT_STRING },
|
||||
{ "aboutURI" : URI_BAD_FILE_CACHED, "testString" : ABOUT_BLANK },
|
||||
{ "aboutURI" : URI_GOOD, "testString" : GOOD_ABOUT_STRING }
|
||||
];
|
||||
|
||||
var browser = null;
|
||||
var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
|
||||
.getService(Ci.nsIAboutNewTabService);
|
||||
|
||||
function pushPrefs(...aPrefs) {
|
||||
return new Promise((resolve) => {
|
||||
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* run tests with input from TESTS
|
||||
*/
|
||||
function doTest(aExpectedString, reload, aUrl, aNewTabPref) {
|
||||
// set about:newtab location for this test if it's a newtab test
|
||||
if (aNewTabPref) {
|
||||
aboutNewTabService.newTabURL = aNewTabPref;
|
||||
}
|
||||
|
||||
// set prefs
|
||||
yield pushPrefs(
|
||||
["browser.newtabpage.remote.content-signing-test", true],
|
||||
["browser.newtabpage.remote", true], [
|
||||
"browser.newtabpage.remote.keys",
|
||||
"RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" +
|
||||
"a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" +
|
||||
"B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" +
|
||||
"CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" +
|
||||
"3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" +
|
||||
"kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" +
|
||||
"Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" +
|
||||
"Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6"
|
||||
]);
|
||||
|
||||
// start the test
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: aUrl,
|
||||
},
|
||||
function * (browser) {
|
||||
// check if everything's set correct for testing
|
||||
ok(Services.prefs.getBoolPref(
|
||||
"browser.newtabpage.remote.content-signing-test"),
|
||||
"sanity check: remote newtab signing test should be used");
|
||||
ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
|
||||
"sanity check: remote newtab should be used");
|
||||
// we only check this if we really do a newtab test
|
||||
if (aNewTabPref) {
|
||||
ok(aboutNewTabService.overridden,
|
||||
"sanity check: default URL for about:newtab should be overriden");
|
||||
is(aboutNewTabService.newTabURL, aNewTabPref,
|
||||
"sanity check: default URL for about:newtab should return the new URL");
|
||||
}
|
||||
yield ContentTask.spawn(
|
||||
browser, aExpectedString, function * (aExpectedString) {
|
||||
ok(content.document.documentElement.innerHTML.includes(aExpectedString),
|
||||
"Expect the following value in the result\n" + aExpectedString +
|
||||
"\nand got " + content.document.documentElement.innerHTML);
|
||||
});
|
||||
|
||||
// for good test cases we check if a reload fails if the remote page
|
||||
// changed from valid to invalid in the meantime
|
||||
if (reload) {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: INVALIDATE_FILE,
|
||||
},
|
||||
function * (browser2) {
|
||||
yield ContentTask.spawn(browser2, null, function * () {
|
||||
ok(content.document.documentElement.innerHTML.includes("Done"),
|
||||
"Expect the following value in the result\n" + "Done" +
|
||||
"\nand got " + content.document.documentElement.innerHTML);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
browser.reload();
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
aExpectedString = ABOUT_BLANK;
|
||||
yield ContentTask.spawn(browser, aExpectedString,
|
||||
function * (aExpectedString) {
|
||||
ok(content.document.documentElement.innerHTML.includes(aExpectedString),
|
||||
"Expect the following value in the result\n" + aExpectedString +
|
||||
"\nand got " + content.document.documentElement.innerHTML);
|
||||
}
|
||||
);
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: VALIDATE_FILE,
|
||||
},
|
||||
function * (browser2) {
|
||||
yield ContentTask.spawn(browser2, null, function * () {
|
||||
ok(content.document.documentElement.innerHTML.includes("Done"),
|
||||
"Expect the following value in the result\n" + "Done" +
|
||||
"\nand got " + content.document.documentElement.innerHTML);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_task(function * test() {
|
||||
// run tests from TESTS
|
||||
for (let i = 0; i < TESTS.length; i++) {
|
||||
let testCase = TESTS[i];
|
||||
let url = "", aNewTabPref = "";
|
||||
let reload = false;
|
||||
let aExpectedString = testCase.testString;
|
||||
if (testCase.aboutURI) {
|
||||
url = ABOUT_NEWTAB_URI;
|
||||
aNewTabPref = testCase.aboutURI;
|
||||
if (aExpectedString == GOOD_ABOUT_STRING) {
|
||||
reload = true;
|
||||
}
|
||||
} else {
|
||||
url = testCase.url;
|
||||
}
|
||||
|
||||
yield doTest(aExpectedString, reload, url, aNewTabPref);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testpage for bug 1226928</title>
|
||||
</head>
|
||||
<body>
|
||||
Just a fully good testpage for Bug 1226928<br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testpage for bug 1226928</title>
|
||||
</head>
|
||||
<body>
|
||||
Just a bad testpage for Bug 1226928<br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX
|
|
@ -0,0 +1 @@
|
|||
MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg
|
|
@ -0,0 +1 @@
|
|||
XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O
|
|
@ -0,0 +1,179 @@
|
|||
// sjs for remote about:newtab (bug 1226928)
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
const path = "browser/dom/security/test/contentverifier/";
|
||||
|
||||
const goodFileName = "file_about_newtab.html";
|
||||
const goodFileBase = path + goodFileName;
|
||||
const goodFile = FileUtils.getDir("TmpD", [], true);
|
||||
goodFile.append(goodFileName);
|
||||
const goodSignature = path + "file_about_newtab_good_signature";
|
||||
const goodKeyId = "RemoteNewTab";
|
||||
|
||||
const badFile = path + "file_about_newtab_bad.html";
|
||||
const brokenSignature = path + "file_about_newtab_broken_signature";
|
||||
const badSignature = path + "file_about_newtab_bad_signature";
|
||||
const badKeyId = "OldRemoteNewTabKey";
|
||||
|
||||
// we copy the file to serve as newtab to a temp directory because
|
||||
// we modify it during tests.
|
||||
setupTestFile();
|
||||
|
||||
function setupTestFile() {
|
||||
let tempFile = FileUtils.getDir("TmpD", [], true);
|
||||
tempFile.append(goodFileName);
|
||||
if (!tempFile.exists()) {
|
||||
let fileIn = getFileName(goodFileBase, "CurWorkD");
|
||||
fileIn.copyTo(FileUtils.getDir("TmpD", [], true), "");
|
||||
}
|
||||
}
|
||||
|
||||
function getFileName(filePath, dir) {
|
||||
// Since it's relative to the cwd of the test runner, we start there and
|
||||
// append to get to the actual path of the file.
|
||||
let testFile =
|
||||
Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get(dir, Components.interfaces.nsILocalFile);
|
||||
let dirs = filePath.split("/");
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
testFile.append(dirs[i]);
|
||||
}
|
||||
return testFile;
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
// Load a file to return it.
|
||||
let testFileStream =
|
||||
Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
testFileStream.init(file, -1, 0, 0);
|
||||
return NetUtil.readInputStreamToString(testFileStream,
|
||||
testFileStream.available());
|
||||
}
|
||||
|
||||
function appendToFile(aFile, content) {
|
||||
try {
|
||||
let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_APPEND |
|
||||
FileUtils.MODE_WRONLY);
|
||||
file.write(content, content.length);
|
||||
file.close();
|
||||
} catch (e) {
|
||||
dump(">>> Error in appendToFile "+e);
|
||||
return "Error";
|
||||
}
|
||||
return "Done";
|
||||
}
|
||||
|
||||
function truncateFile(aFile, length) {
|
||||
let fileIn = loadFile(aFile);
|
||||
fileIn = fileIn.slice(0, -length);
|
||||
|
||||
try {
|
||||
let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_WRONLY |
|
||||
FileUtils.MODE_TRUNCATE);
|
||||
file.write(fileIn, fileIn.length);
|
||||
file.close();
|
||||
} catch (e) {
|
||||
dump(">>> Error in truncateFile "+e);
|
||||
return "Error";
|
||||
}
|
||||
return "Done";
|
||||
}
|
||||
|
||||
/*
|
||||
* handle requests of the following form:
|
||||
* sig=good&key=good&file=good&header=good&cached=no to serve pages with
|
||||
* content signatures
|
||||
*
|
||||
* it further handles invalidateFile=yep and validateFile=yep to change the
|
||||
* served file
|
||||
*/
|
||||
function handleRequest(request, response) {
|
||||
let params = new URLSearchParams(request.queryString);
|
||||
let keyType = params.get("key");
|
||||
let signatureType = params.get("sig");
|
||||
let fileType = params.get("file");
|
||||
let headerType = params.get("header");
|
||||
let cached = params.get("cached");
|
||||
let invalidateFile = params.get("invalidateFile");
|
||||
let validateFile = params.get("validateFile");
|
||||
|
||||
// if invalidateFile is set, this doesn't actually return a newtab page
|
||||
// but changes the served file to invalidate the signature
|
||||
// NOTE: make sure to make the file valid again afterwards!
|
||||
if (invalidateFile) {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
let r = appendToFile(goodFile, "!");
|
||||
response.write(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// if validateFile is set, this doesn't actually return a newtab page
|
||||
// but changes the served file to make the signature valid again
|
||||
if (validateFile) {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
let r = truncateFile(goodFile, 1);
|
||||
response.write(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid confusing cache behaviours
|
||||
if (!cached) {
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
} else {
|
||||
response.setHeader("Cache-Control", "max-age=3600", false);
|
||||
}
|
||||
|
||||
// send HTML to test allowed/blocked behaviours
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
|
||||
// set signature header and key for Content-Signature header
|
||||
/* By default a good content-signature header is returned. Any broken return
|
||||
* value has to be indicated in the url.
|
||||
*/
|
||||
let csHeader = "";
|
||||
let keyId = goodKeyId;
|
||||
let signature = goodSignature;
|
||||
let file = goodFile;
|
||||
if (keyType == "bad") {
|
||||
keyId = badKeyId;
|
||||
}
|
||||
if (signatureType == "bad") {
|
||||
signature = badSignature;
|
||||
} else if (signatureType == "broken") {
|
||||
signature = brokenSignature;
|
||||
}
|
||||
if (fileType == "bad") {
|
||||
file = getFileName(badFile, "CurWorkD");
|
||||
}
|
||||
|
||||
if (headerType == "good") {
|
||||
// a valid content-signature header
|
||||
csHeader = "keyid=" + keyId + ";p384ecdsa=" +
|
||||
loadFile(getFileName(signature, "CurWorkD"));
|
||||
} else if (headerType == "error") {
|
||||
// this content-signature header is missing ; before p384ecdsa
|
||||
csHeader = "keyid=" + keyId + "p384ecdsa=" +
|
||||
loadFile(getFileName(signature, "CurWorkD"));
|
||||
} else if (headerType == "errorInKeyid") {
|
||||
// this content-signature header is missing the keyid directive
|
||||
csHeader = "keid=" + keyId + ";p384ecdsa=" +
|
||||
loadFile(getFileName(signature, "CurWorkD"));
|
||||
} else if (headerType == "errorInSignature") {
|
||||
// this content-signature header is missing the p384ecdsa directive
|
||||
csHeader = "keyid=" + keyId + ";p385ecdsa=" +
|
||||
loadFile(getFileName(signature, "CurWorkD"));
|
||||
}
|
||||
|
||||
if (csHeader) {
|
||||
response.setHeader("Content-Signature", csHeader, false);
|
||||
}
|
||||
let result = loadFile(file);
|
||||
|
||||
response.write(result);
|
||||
}
|
Двоичный файл не отображается.
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQAIg==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDAzX2TrGOr0WE92AbAl+nqnpqh25pKCLYNMTV2hJHztrkVPWOp8w0mh
|
||||
scIodK8RMpagBwYFK4EEACKhZANiAATiTcWYbt0Wg63dO7OXvpptNG0ryxv+v+Js
|
||||
JJ5Upr3pFus5fZyKxzP9NPzB+oFhL/xw3jMx7X5/vBGaQ2sJSiNlHVkqZgzYF6JQ
|
||||
4yUyiqTY7v67CyfUPA1BJg/nxOS9m3o=
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -24,5 +24,6 @@ MOCHITEST_CHROME_MANIFESTS += [
|
|||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'contentverifier/browser.ini',
|
||||
'csp/browser.ini',
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче