diff options
Diffstat (limited to 'dom/security/test/contentverifier')
19 files changed, 670 insertions, 0 deletions
diff --git a/dom/security/test/contentverifier/browser.ini b/dom/security/test/contentverifier/browser.ini new file mode 100644 index 000000000..c41c2e8e8 --- /dev/null +++ b/dom/security/test/contentverifier/browser.ini @@ -0,0 +1,19 @@ +[DEFAULT] +support-files = + file_contentserver.sjs + file_about_newtab.html + file_about_newtab_bad.html + file_about_newtab_bad_csp.html + file_about_newtab_bad_csp_signature + 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 + goodChain.pem + head.js + script.js + style.css + +[browser_verify_content_about_newtab.js] +[browser_verify_content_about_newtab2.js] diff --git a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js new file mode 100644 index 000000000..f63726ef8 --- /dev/null +++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js @@ -0,0 +1,20 @@ + +const TESTS = [ + // { newtab (aboutURI) or regular load (url) : url, + // testStrings : expected strings in the loaded page } + { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] }, + { "aboutURI" : URI_ERROR_HEADER, "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, + ]}, + { "aboutURI" : URI_BAD_CSP, "testStrings" : [CSP_TEST_SUCCESS_STRING] }, + { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] }, +]; + +add_task(runTests); diff --git a/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js new file mode 100644 index 000000000..a35ff6660 --- /dev/null +++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js @@ -0,0 +1,19 @@ + +const TESTS = [ + // { newtab (aboutURI) or regular load (url) : url, + // 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_X5U, "testStrings" : [ABOUT_BLANK] }, + { "aboutURI" : URI_HTTP_X5U, "testStrings" : [ABOUT_BLANK] }, + { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] }, + { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] }, + { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] }, +]; + +add_task(runTests); diff --git a/dom/security/test/contentverifier/file_about_newtab.html b/dom/security/test/contentverifier/file_about_newtab.html new file mode 100644 index 000000000..f274743b9 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab.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 fully good testpage for Bug 1226928<br/> +</body> +</html>
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad.html b/dom/security/test/contentverifier/file_about_newtab_bad.html new file mode 100644 index 000000000..45899f4f4 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad.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>
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_csp.html b/dom/security/test/contentverifier/file_about_newtab_bad_csp.html new file mode 100644 index 000000000..f8044b73f --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Testpage for CSP violation (inline script)</title> +</head> +<body> + CSP violation test succeeded. + <script> + // This inline script would override the success string if loaded. + document.body.innerHTML = "CSP violation test failed."; + </script> +</body> +</html>
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature new file mode 100644 index 000000000..ded42dc9f --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature @@ -0,0 +1 @@ +oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_signature b/dom/security/test/contentverifier/file_about_newtab_bad_signature new file mode 100644 index 000000000..73a3c1e34 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_bad_signature @@ -0,0 +1 @@ +KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_broken_signature b/dom/security/test/contentverifier/file_about_newtab_broken_signature new file mode 100644 index 000000000..468a167ff --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_broken_signature @@ -0,0 +1 @@ +MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_good_signature b/dom/security/test/contentverifier/file_about_newtab_good_signature new file mode 100644 index 000000000..d826d49c3 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_good_signature @@ -0,0 +1 @@ +HUndgHvxHNMiAe1SXoeyOOraUJCdxHqWkAYTu0Cq1KpAHcWZEVelNTvyXGbTLWj8btsmqNLAm08UlyK43q_2oO9DQfez3Fo8DhsKvm7TqgSXCkhUoxsYNanxWXhqw-Jw
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_about_newtab_sri.html b/dom/security/test/contentverifier/file_about_newtab_sri.html new file mode 100644 index 000000000..4415c533a --- /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> diff --git a/dom/security/test/contentverifier/file_about_newtab_sri_signature b/dom/security/test/contentverifier/file_about_newtab_sri_signature new file mode 100644 index 000000000..b7ac17944 --- /dev/null +++ b/dom/security/test/contentverifier/file_about_newtab_sri_signature @@ -0,0 +1 @@ +yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO
\ No newline at end of file diff --git a/dom/security/test/contentverifier/file_contentserver.sjs b/dom/security/test/contentverifier/file_contentserver.sjs new file mode 100644 index 000000000..3ea49cdde --- /dev/null +++ b/dom/security/test/contentverifier/file_contentserver.sjs @@ -0,0 +1,261 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* 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/. */ +// sjs for remote about:newtab (bug 1226928) +"use strict"; + +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 goodX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\""; + +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 badX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=bad\""; +const httpX5UString = "\"http://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\""; + +const sriFile = path + "file_about_newtab_sri.html"; +const sriSignature = path + "file_about_newtab_sri_signature"; + +const badCspFile = path + "file_about_newtab_bad_csp.html"; +const badCspSignature = path + "file_about_newtab_bad_csp_signature"; + +// This cert chain is copied from +// security/manager/ssl/tests/unit/test_content_signing/ +// using the certificates +// * content_signing_remote_newtab_ee.pem +// * content_signing_int.pem +// * content_signing_root.pem +const goodCertChainPath = path + "goodChain.pem"; + +const tempFileNames = [goodFileName, scriptFileName, cssFileName]; + +// we copy the file to serve as newtab to a temp directory because +// we modify it during tests. +setupTestFiles(); + +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"]. + 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"; +} + +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 + */ +function handleRequest(request, response) { + let params = new URLSearchParams(request.queryString); + let x5uType = params.get("x5u"); + 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"); + let x5uParam = params.get("x5u"); + + 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); + 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); + response.write(r); + return; + } + + // we have to return the certificate chain on request for the x5u parameter + if (x5uParam && x5uParam == "default") { + response.setHeader("Cache-Control", "max-age=216000", false); + response.setHeader("Content-Type", "text/plain", false); + response.write(loadFile(getFileName(goodCertChainPath, "CurWorkD"))); + 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 x5uString = goodX5UString; + let signature = goodSignature; + let file = goodFile; + if (x5uType == "bad") { + x5uString = badX5UString; + } else if (x5uType == "http") { + x5uString = httpX5UString; + } + if (signatureType == "bad") { + signature = badSignature; + } else if (signatureType == "broken") { + signature = brokenSignature; + } else if (signatureType == "sri") { + signature = sriSignature; + } else if (signatureType == "bad-csp") { + signature = badCspSignature; + } + if (fileType == "bad") { + file = getFileName(badFile, "CurWorkD"); + } else if (fileType == "sri") { + file = getFileName(sriFile, "CurWorkD"); + } else if (fileType == "bad-csp") { + file = getFileName(badCspFile, "CurWorkD"); + } + + if (headerType == "good") { + // a valid content-signature header + csHeader = "x5u=" + x5uString + ";p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "error") { + // this content-signature header is missing ; before p384ecdsa + csHeader = "x5u=" + x5uString + "p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "errorInX5U") { + // this content-signature header is missing the keyid directive + csHeader = "x6u=" + x5uString + ";p384ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } else if (headerType == "errorInSignature") { + // this content-signature header is missing the p384ecdsa directive + csHeader = "x5u=" + x5uString + ";p385ecdsa=" + + loadFile(getFileName(signature, "CurWorkD")); + } + + if (csHeader) { + response.setHeader("Content-Signature", csHeader, false); + } + let result = loadFile(file); + + response.write(result); +} diff --git a/dom/security/test/contentverifier/goodChain.pem b/dom/security/test/contentverifier/goodChain.pem new file mode 100644 index 000000000..d5047cfc2 --- /dev/null +++ b/dom/security/test/contentverifier/goodChain.pem @@ -0,0 +1,51 @@ +-----BEGIN CERTIFICATE----- +MIICUzCCAT2gAwIBAgIUJ1BtYqWRwUsVaZCGPp9eTHIC04QwCwYJKoZIhvcNAQEL +MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE1MTEyODAwMDAwMFoYDzIwMTgwMjA1 +MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q +nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD +dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90 +ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B +AQsDggEBALiLck6k50ok9ahVq45P3feY1PeUXcIYZkJd8aPDYM+0kfg5+JyJBykA +mtHWPE1QQjs7VRMfaLfu04E4UJMI2V1AON1qtgR9BQLctW85KFACg2omfiCKwJh0 +5Q8cxBFx9BpNMayqLJwHttB6oluxZFTB8CL/hfpbYpHz1bMEDCVSRP588YBrc8mV +OLqzQK+k3ewwGvfD6SvXmTny37MxqwxdTPFJNnpqzKAsQIvz8Skic9BkA1NFk0Oq +lsKKoiibbOCmwS9XY/laAkBaC3winuhciYAC0ImAopZ4PBCU0AOHGrNbhZXWYQxt +uHBj34FqvIRCqgM06JCEwN0ULgix4kI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC0TCCAbugAwIBAgIUPcKbBQpKwTzrrlqzM+d3z5DWiNUwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw +MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x +nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM +wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF +4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20 +yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx +j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT +BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADDPjITgz8joxLRW +wpLxELKSgO/KQ6iAXztjMHq9ovT7Fy0fqBnQ1mMVFr+sBXLgtUCM45aip6PjhUXc +zs5Dq5STg+kz7qtmAjEQvOPcyictbgdu/K7+uMhXQhlzhOgyW88Uk5vrAezNTc/e +TvSmWp1FcgVAfaeMN/90nzD1KIHoUt7zqZIz9ub8jXPVzQNZq4vh33smZhmbdTdV +DaHUyef5cR1VTEGB+L1qzUIQqpHmD4UkMNP1nYedWfauiQhRt6Ql3rJSCRuEvsOA +iBTJlwai/EFwfyfHkOV2GNgv+A5wHHEjBtF5c4PCxQEL5Vw+mfZHLsDVqF3279ZY +lQ6jQ9g= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICzTCCAbegAwIBAgIUKRLJoCmk0A6PHrNc8CxFn//4BYcwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw +MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu +Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO +7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf +qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt +HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx +uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud +JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAABgMK6EyVIXTjD5qaxPO +DWz6yREACmAQBcowKWvfhwgi27DPSXyFGDbzTPEo+7RrIcXJkVAhLouGT51fCwTZ +zb6Sgf6ztX7VSppY9AT4utvlZKP1xQ5WhIYsMtdHCHLHIkRjeWyoBEfUx50UXNLK +Snl+A02GKYWiX+TLLg2DPN2s7v/mm8NLMQNgUlL7KakB2FHFyPa8otPpL4llg7UJ +iBTVQ0c3JoiVbwZaY1Z8QinfMXUrTK9egUC4BAcId1dE8glzA5RRlw1fTLWpGApt +hUmbDnl9N2a9NhGX323ypNzIATexafipzWe7bc4u/+bFdrUqnKUoEka73pZBdHdA +FQ== +-----END CERTIFICATE----- diff --git a/dom/security/test/contentverifier/head.js b/dom/security/test/contentverifier/head.js new file mode 100644 index 000000000..d9637d18b --- /dev/null +++ b/dom/security/test/contentverifier/head.js @@ -0,0 +1,210 @@ +/* + * 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&x5u=good&file=good&header=good"; + +const INVALIDATE_FILE = BASE + "invalidateFile=yep"; +const VALIDATE_FILE = BASE + "validateFile=yep"; + +const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header="; +const URI_ERROR_HEADER = URI_HEADER_BASE + "error"; +const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U"; +const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature"; +const URI_NO_HEADER = URI_HEADER_BASE + "noHeader"; + +const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good"; +const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good"; +const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good"; +const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good"; +const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good"; +const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad"; +const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good"; + +const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=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&x5u=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 CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded."; + +// Needs to sync with pref "security.signed_content.CSP.default". +const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`; + +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(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], + ["browser.newtabpage.remote", true], + ["security.content.signature.root_hash", + "CC:BE:04:87:74:B2:98:24:4A:C6:7A:71:BC:6F:DB:D6:C0:48:17:29:57:51:96:47:38:CC:24:C8:E4:F9:DD:CB"]); + + if (aNewTabPref === URI_BAD_CSP) { + // Use stricter CSP to test CSP violation. + yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]); + } else { + // Use weaker CSP to test normal content. + yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]); + } + + // 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"); + } + + // Every valid remote newtab page must have built-in CSP. + let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) && + (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI)); + + if (shouldHaveCSP) { + is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP, + "Valid remote newtab page must have built-in CSP."); + } + + yield ContentTask.spawn( + 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, + }, + 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); + + 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) { + 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); + }); + } + ); + } + } + ); +} + +function runTests() { + // run tests from TESTS + for (let i = 0; i < TESTS.length; i++) { + let testCase = TESTS[i]; + let url = "", aNewTabPref = ""; + let reload = false; + var aExpectedStrings = testCase.testStrings; + if (testCase.aboutURI) { + url = ABOUT_NEWTAB_URI; + aNewTabPref = testCase.aboutURI; + if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) { + reload = true; + } + } else { + url = testCase.url; + } + + yield doTest(aExpectedStrings, reload, url, aNewTabPref); + } +} diff --git a/dom/security/test/contentverifier/script.js b/dom/security/test/contentverifier/script.js new file mode 100644 index 000000000..8fd8f96b2 --- /dev/null +++ b/dom/security/test/contentverifier/script.js @@ -0,0 +1 @@ +var load=true; diff --git a/dom/security/test/contentverifier/signature.der b/dom/security/test/contentverifier/signature.der Binary files differnew file mode 100644 index 000000000..011b94142 --- /dev/null +++ b/dom/security/test/contentverifier/signature.der diff --git a/dom/security/test/contentverifier/sk.pem b/dom/security/test/contentverifier/sk.pem new file mode 100644 index 000000000..2ed514b9f --- /dev/null +++ b/dom/security/test/contentverifier/sk.pem @@ -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----- diff --git a/dom/security/test/contentverifier/style.css b/dom/security/test/contentverifier/style.css new file mode 100644 index 000000000..c7ab9ecff --- /dev/null +++ b/dom/security/test/contentverifier/style.css @@ -0,0 +1,3 @@ +#red-text { + color: red; +} |