Bug 1235572 - Tests of enforcing SRI on remote about:newtab r?francois
MozReview-Commit-ID: 6epw8D4M0FX
--- a/dom/security/test/contentverifier/browser.ini
+++ b/dom/security/test/contentverifier/browser.ini
@@ -1,10 +1,14 @@
[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
+ file_about_newtab_sri.html
+ file_about_newtab_sri_signature
+ script.js
+ style.css
[browser_verify_content_about_newtab.js]
--- a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
@@ -38,48 +38,66 @@ const URI_BAD_FILE = BASE + "sig=good&ke
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 URI_CLEANUP = BASE + "cleanup=true";
+const CLEANUP_DONE = "Done";
+
+const URI_SRI = BASE + "sig=sri&key=good&file=sri&header=good";
+const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
+const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
+const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
+const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
+const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
+const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
+
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 }
+ // testStrings : expected strings in the loaded page }
+ { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+ { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_KEY, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
+ { "url" : URI_BAD_FILE_CACHED, "testStrings" : [BAD_ABOUT_STRING] },
+ { "aboutURI" : URI_BAD_FILE_CACHED, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+ { "aboutURI" : URI_SRI, "testStrings" : [
+ STYLESHEET_WITHOUT_SRI_BLOCKED,
+ STYLESHEET_WITH_SRI_LOADED,
+ SCRIPT_WITHOUT_SRI_BLOCKED,
+ SCRIPT_WITH_SRI_LOADED,
+ ]},
+ { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
];
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) {
+function doTest(aExpectedStrings, 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],
@@ -110,20 +128,22 @@ function doTest(aExpectedString, reload,
// 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);
+ browser, aExpectedStrings, function * (aExpectedStrings) {
+ for (let expectedString of aExpectedStrings) {
+ ok(content.document.documentElement.innerHTML.includes(expectedString),
+ "Expect the following value in the result\n" + expectedString +
+ "\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,
@@ -135,22 +155,32 @@ function doTest(aExpectedString, reload,
"\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);
+ let expectedStrings = [ABOUT_BLANK];
+ if (aNewTabPref == URI_SRI) {
+ expectedStrings = [
+ STYLESHEET_WITHOUT_SRI_BLOCKED,
+ STYLESHEET_WITH_SRI_BLOCKED,
+ SCRIPT_WITHOUT_SRI_BLOCKED,
+ SCRIPT_WITH_SRI_BLOCKED
+ ];
+ }
+ yield ContentTask.spawn(browser, expectedStrings,
+ function * (expectedStrings) {
+ for (let expectedString of expectedStrings) {
+ ok(content.document.documentElement.innerHTML.includes(expectedString),
+ "Expect the following value in the result\n" + expectedString +
+ "\nand got " + content.document.documentElement.innerHTML);
+ }
}
);
yield BrowserTestUtils.withNewTab({
gBrowser,
url: VALIDATE_FILE,
},
function * (browser2) {
@@ -167,22 +197,22 @@ function doTest(aExpectedString, reload,
}
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;
+ var aExpectedStrings = testCase.testStrings;
if (testCase.aboutURI) {
url = ABOUT_NEWTAB_URI;
aNewTabPref = testCase.aboutURI;
- if (aExpectedString == GOOD_ABOUT_STRING) {
+ if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
reload = true;
}
} else {
url = testCase.url;
}
- yield doTest(aExpectedString, reload, url, aNewTabPref);
+ yield doTest(aExpectedStrings, reload, url, aNewTabPref);
}
});
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_sri.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1235572 -->
+<head>
+ <meta charset="utf-8">
+ <title>Testpage for bug 1235572</title>
+ <script>
+ function loaded(resource) {
+ document.getElementById("result").innerHTML += resource + " loaded\n";
+ }
+ function blocked(resource) {
+ document.getElementById("result").innerHTML += resource + " blocked\n";
+ }
+ </script>
+</head>
+<body>
+ Testing script loading without SRI for Bug 1235572<br/>
+ <div id="result"></div>
+
+ <!-- use css1 and css2 to make urls different to avoid the resource being cached-->
+ <link rel="stylesheet" href="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=css1"
+ onload="loaded('Stylesheet without SRI')"
+ onerror="blocked('Stylesheet without SRI')">
+ <link rel="stylesheet" href="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=css2"
+ integrity="sha384-/6Tvxh7SX39y62qePcvYoi5Vrf0lK8Ix3wJFLCYKI5KNJ5wIlCR8UsFC1OXwmwgd"
+ onload="loaded('Stylesheet with SRI')"
+ onerror="blocked('Stylesheet with SRI')">
+ <script src="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=script"
+ onload="loaded('Script without SRI')"
+ onerror="blocked('Script without SRI')"></script>
+ <script src="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=script"
+ integrity="sha384-zDCkvKOHXk8mM6Nk07oOGXGME17PA4+ydFw+hq0r9kgF6ZDYFWK3fLGPEy7FoOAo"
+ onload="loaded('Script with SRI')"
+ onerror="blocked('Script with SRI')"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_sri_signature
@@ -0,0 +1,1 @@
+i5jOnrZWwyNwrTcIjfJ6fUR-8MhhvhtMvQbdrUD7j8aHTybNolv25v9NwJAT6rVU6kgkxmD_st9Kla086CQmzYQdLhKfzgLbTDXz0-1j23fQnyjsP1_4MNIu2xTea11p
\ No newline at end of file
--- a/dom/security/test/contentverifier/file_contentserver.sjs
+++ b/dom/security/test/contentverifier/file_contentserver.sjs
@@ -9,31 +9,40 @@ const path = "browser/dom/security/test/
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 scriptFileName = "script.js";
+const cssFileName = "style.css";
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";
+const sriFile = path + "file_about_newtab_sri.html";
+const sriSignature = path + "file_about_newtab_sri_signature";
+
+const tempFileNames = [goodFileName, scriptFileName, cssFileName];
+
// we copy the file to serve as newtab to a temp directory because
// we modify it during tests.
-setupTestFile();
+setupTestFiles();
-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 setupTestFiles() {
+ for (let fileName of tempFileNames) {
+ let tempFile = FileUtils.getDir("TmpD", [], true);
+ tempFile.append(fileName);
+ if (!tempFile.exists()) {
+ let fileIn = getFileName(path + fileName, "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"].
@@ -80,16 +89,24 @@ function truncateFile(aFile, length) {
file.close();
} catch (e) {
dump(">>> Error in truncateFile "+e);
return "Error";
}
return "Done";
}
+function cleanupTestFiles() {
+ for (let fileName of tempFileNames) {
+ let tempFile = FileUtils.getDir("TmpD", [], true);
+ tempFile.append(fileName);
+ tempFile.remove(true);
+ }
+}
+
/*
* 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
*/
@@ -97,32 +114,61 @@ 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");
+ let resource = params.get("resource");
+
+ if (params.get("cleanup")) {
+ cleanupTestFiles();
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("Done");
+ return;
+ }
+
+ if (resource) {
+ if (resource == "script") {
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write(loadFile(getFileName(scriptFileName, "TmpD")));
+ } else { // resource == "css1" || resource == "css2"
+ response.setHeader("Content-Type", "text/css", false);
+ response.write(loadFile(getFileName(cssFileName, "TmpD")));
+ }
+ return;
+ }
// 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) {
+ let r = "Done";
+ for (let fileName of tempFileNames) {
+ if (appendToFile(getFileName(fileName, "TmpD"), "!") != "Done") {
+ r = "Error";
+ }
+ }
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) {
+ let r = "Done";
+ for (let fileName of tempFileNames) {
+ if (truncateFile(getFileName(fileName, "TmpD"), 1) != "Done") {
+ r = "Error";
+ }
+ }
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 {
@@ -142,19 +188,23 @@ function handleRequest(request, response
let file = goodFile;
if (keyType == "bad") {
keyId = badKeyId;
}
if (signatureType == "bad") {
signature = badSignature;
} else if (signatureType == "broken") {
signature = brokenSignature;
+ } else if (signatureType == "sri") {
+ signature = sriSignature;
}
if (fileType == "bad") {
file = getFileName(badFile, "CurWorkD");
+ } else if (fileType == "sri") {
+ file = getFileName(sriFile, "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
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/script.js
@@ -0,0 +1,1 @@
+var load=true;
new file mode 100644
--- /dev/null
+++ b/dom/security/test/contentverifier/style.css
@@ -0,0 +1,3 @@
+#red-text {
+ color: red;
+}