import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -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]

View file

@ -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);

View file

@ -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);

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1 @@
oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd

View file

@ -0,0 +1 @@
KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX

View file

@ -0,0 +1 @@
MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg

View file

@ -0,0 +1 @@
HUndgHvxHNMiAe1SXoeyOOraUJCdxHqWkAYTu0Cq1KpAHcWZEVelNTvyXGbTLWj8btsmqNLAm08UlyK43q_2oO9DQfez3Fo8DhsKvm7TqgSXCkhUoxsYNanxWXhqw-Jw

View file

@ -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>

View file

@ -0,0 +1 @@
yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO

View file

@ -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);
}

View file

@ -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-----

View file

@ -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);
}
}

View file

@ -0,0 +1 @@
var load=true;

Binary file not shown.

View file

@ -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-----

View file

@ -0,0 +1,3 @@
#red-text {
color: red;
}

View file

@ -0,0 +1,49 @@
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
if ("setState" in query) {
setState("test/dom/security/test_CrossSiteXHR_cache:secData",
query.setState);
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/plain", false);
response.write("hi");
return;
}
var isPreflight = request.method == "OPTIONS";
// Send response
secData =
eval(getState("test/dom/security/test_CrossSiteXHR_cache:secData"));
if (secData.allowOrigin)
response.setHeader("Access-Control-Allow-Origin", secData.allowOrigin);
if (secData.withCred)
response.setHeader("Access-Control-Allow-Credentials", "true");
if (isPreflight) {
if (secData.allowHeaders)
response.setHeader("Access-Control-Allow-Headers", secData.allowHeaders);
if (secData.allowMethods)
response.setHeader("Access-Control-Allow-Methods", secData.allowMethods);
if (secData.cacheTime)
response.setHeader("Access-Control-Max-Age", secData.cacheTime.toString());
return;
}
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "application/xml", false);
response.write("<res>hello pass</res>\n");
}

View file

@ -0,0 +1,121 @@
<!DOCTYPE HTML>
<!--
NOTE! The content of this file is duplicated in file_CrossSiteXHR_inner.jar
and file_CrossSiteXHR_inner_data.sjs
Please update those files if you update this one.
-->
<html>
<head>
<script>
function trimString(stringValue) {
return stringValue.replace(/^\s+|\s+$/g, '');
};
window.addEventListener("message", function(e) {
sendData = null;
req = eval(e.data);
var res = {
didFail: false,
events: [],
progressEvents: 0,
status: 0,
responseText: "",
statusText: "",
responseXML: null,
sendThrew: false
};
var xhr = new XMLHttpRequest();
for (type of ["load", "abort", "error", "loadstart", "loadend"]) {
xhr.addEventListener(type, function(e) {
res.events.push(e.type);
}, false);
}
xhr.addEventListener("readystatechange", function(e) {
res.events.push("rs" + xhr.readyState);
}, false);
xhr.addEventListener("progress", function(e) {
res.progressEvents++;
}, false);
if (req.uploadProgress) {
xhr.upload.addEventListener(req.uploadProgress, function(e) {
res.progressEvents++;
}, false);
}
xhr.onerror = function(e) {
res.didFail = true;
};
xhr.onloadend = function (event) {
res.status = xhr.status;
try {
res.statusText = xhr.statusText;
} catch (e) {
delete(res.statusText);
}
res.responseXML = xhr.responseXML ?
(new XMLSerializer()).serializeToString(xhr.responseXML) :
null;
res.responseText = xhr.responseText;
res.responseHeaders = {};
for (responseHeader in req.responseHeaders) {
res.responseHeaders[responseHeader] =
xhr.getResponseHeader(responseHeader);
}
res.allResponseHeaders = {};
var splitHeaders = xhr.getAllResponseHeaders().split("\r\n");
for (var i = 0; i < splitHeaders.length; i++) {
var headerValuePair = splitHeaders[i].split(":");
if(headerValuePair[1] != null) {
var headerName = trimString(headerValuePair[0]);
var headerValue = trimString(headerValuePair[1]);
res.allResponseHeaders[headerName] = headerValue;
}
}
post(e, res);
}
if (req.withCred)
xhr.withCredentials = true;
if (req.body)
sendData = req.body;
res.events.push("opening");
// Allow passign in falsy usernames/passwords so we can test them
try {
xhr.open(req.method, req.url, true,
("username" in req) ? req.username : "",
("password" in req) ? req.password : "aa");
} catch (ex) {
res.didFail = true;
post(e, res);
}
for (header in req.headers) {
xhr.setRequestHeader(header, req.headers[header]);
}
res.events.push("sending");
try {
xhr.send(sendData);
} catch (ex) {
res.didFail = true;
res.sendThrew = true;
post(e, res);
}
}, false);
function post(e, res) {
e.source.postMessage(res.toSource(), "http://mochi.test:8888");
}
</script>
</head>
<body>
Inner page
</body>
</html>

Binary file not shown.

View file

@ -0,0 +1,103 @@
var data = '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
<script>\n\
window.addEventListener("message", function(e) {\n\
\n\
sendData = null;\n\
\n\
req = eval(e.data);\n\
var res = {\n\
didFail: false,\n\
events: [],\n\
progressEvents: 0\n\
};\n\
\n\
var xhr = new XMLHttpRequest();\n\
for (type of ["load", "abort", "error", "loadstart", "loadend"]) {\n\
xhr.addEventListener(type, function(e) {\n\
res.events.push(e.type);\n\
}, false);\n\
}\n\
xhr.addEventListener("readystatechange", function(e) {\n\
res.events.push("rs" + xhr.readyState);\n\
}, false);\n\
xhr.addEventListener("progress", function(e) {\n\
res.progressEvents++;\n\
}, false);\n\
if (req.uploadProgress) {\n\
xhr.upload.addEventListener(req.uploadProgress, function(e) {\n\
res.progressEvents++;\n\
}, false);\n\
}\n\
xhr.onerror = function(e) {\n\
res.didFail = true;\n\
};\n\
xhr.onloadend = function (event) {\n\
res.status = xhr.status;\n\
try {\n\
res.statusText = xhr.statusText;\n\
} catch (e) {\n\
delete(res.statusText);\n\
}\n\
res.responseXML = xhr.responseXML ?\n\
(new XMLSerializer()).serializeToString(xhr.responseXML) :\n\
null;\n\
res.responseText = xhr.responseText;\n\
\n\
res.responseHeaders = {};\n\
for (responseHeader in req.responseHeaders) {\n\
res.responseHeaders[responseHeader] =\n\
xhr.getResponseHeader(responseHeader);\n\
}\n\
res.allResponseHeaders = {};\n\
var splitHeaders = xhr.getAllResponseHeaders().split("\\r\\n");\n\
for (var i = 0; i < splitHeaders.length; i++) {\n\
var headerValuePair = splitHeaders[i].split(":");\n\
if(headerValuePair[1] != null){\n\
var headerName = trimString(headerValuePair[0]);\n\
var headerValue = trimString(headerValuePair[1]); \n\
res.allResponseHeaders[headerName] = headerValue;\n\
}\n\
}\n\
post(e, res);\n\
}\n\
\n\
if (req.withCred)\n\
xhr.withCredentials = true;\n\
if (req.body)\n\
sendData = req.body;\n\
\n\
res.events.push("opening");\n\
xhr.open(req.method, req.url, true);\n\
\n\
for (header in req.headers) {\n\
xhr.setRequestHeader(header, req.headers[header]);\n\
}\n\
\n\
res.events.push("sending");\n\
xhr.send(sendData);\n\
\n\
}, false);\n\
\n\
function post(e, res) {\n\
e.source.postMessage(res.toSource(), "*");\n\
}\n\
function trimString(stringValue) {\n\
return stringValue.replace("/^\s+|\s+$/g","");\n\
};\n\
\n\
</script>\n\
</head>\n\
<body>\n\
Inner page\n\
</body>\n\
</html>'
function handleRequest(request, response)
{
response.setStatusLine(null, 302, "Follow me");
response.setHeader("Location", "data:text/html," + escape(data));
response.setHeader("Content-Type", "text/plain");
response.write("Follow that guy!");
}

View file

@ -0,0 +1,179 @@
const CC = Components.Constructor;
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
var isPreflight = request.method == "OPTIONS";
var bodyStream = new BinaryInputStream(request.bodyInputStream);
var bodyBytes = [];
while ((bodyAvail = bodyStream.available()) > 0)
Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
var body = decodeURIComponent(
escape(String.fromCharCode.apply(null, bodyBytes)));
if (query.hop) {
query.hop = parseInt(query.hop, 10);
hops = eval(query.hops);
var curHop = hops[query.hop - 1];
query.allowOrigin = curHop.allowOrigin;
query.allowHeaders = curHop.allowHeaders;
query.allowMethods = curHop.allowMethods;
query.allowCred = curHop.allowCred;
query.noAllowPreflight = curHop.noAllowPreflight;
if (curHop.setCookie) {
query.setCookie = unescape(curHop.setCookie);
}
if (curHop.cookie) {
query.cookie = unescape(curHop.cookie);
}
query.noCookie = curHop.noCookie;
}
// Check that request was correct
if (!isPreflight && query.body && body != query.body) {
sendHttp500(response, "Wrong body. Expected " + query.body + " got " +
body);
return;
}
if (!isPreflight && "headers" in query) {
headers = eval(query.headers);
for(headerName in headers) {
// Content-Type is changed if there was a body
if (!(headerName == "Content-Type" && body) &&
(!request.hasHeader(headerName) ||
request.getHeader(headerName) != headers[headerName])) {
var actual = request.hasHeader(headerName) ? request.getHeader(headerName)
: "<missing header>";
sendHttp500(response,
"Header " + headerName + " had wrong value. Expected " +
headers[headerName] + " got " + actual);
return;
}
}
}
if (isPreflight && "requestHeaders" in query &&
request.getHeader("Access-Control-Request-Headers") != query.requestHeaders) {
sendHttp500(response,
"Access-Control-Request-Headers had wrong value. Expected " +
query.requestHeaders + " got " +
request.getHeader("Access-Control-Request-Headers"));
return;
}
if (isPreflight && "requestMethod" in query &&
request.getHeader("Access-Control-Request-Method") != query.requestMethod) {
sendHttp500(response,
"Access-Control-Request-Method had wrong value. Expected " +
query.requestMethod + " got " +
request.getHeader("Access-Control-Request-Method"));
return;
}
if ("origin" in query && request.getHeader("Origin") != query.origin) {
sendHttp500(response,
"Origin had wrong value. Expected " + query.origin + " got " +
request.getHeader("Origin"));
return;
}
if ("cookie" in query) {
cookies = {};
request.getHeader("Cookie").split(/ *; */).forEach(function (val) {
var [name, value] = val.split('=');
cookies[name] = unescape(value);
});
query.cookie.split(",").forEach(function (val) {
var [name, value] = val.split('=');
if (cookies[name] != value) {
sendHttp500(response,
"Cookie " + name + " had wrong value. Expected " + value +
" got " + cookies[name]);
return;
}
});
}
if (query.noCookie && request.hasHeader("Cookie")) {
sendHttp500(response,
"Got cookies when didn't expect to: " + request.getHeader("Cookie"));
return;
}
// Send response
if (!isPreflight && query.status) {
response.setStatusLine(null, query.status, query.statusMessage);
}
if (isPreflight && query.preflightStatus) {
response.setStatusLine(null, query.preflightStatus, "preflight status");
}
if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight))
response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
if (query.allowCred)
response.setHeader("Access-Control-Allow-Credentials", "true");
if (query.setCookie)
response.setHeader("Set-Cookie", query.setCookie + "; path=/");
if (isPreflight) {
if (query.allowHeaders)
response.setHeader("Access-Control-Allow-Headers", query.allowHeaders);
if (query.allowMethods)
response.setHeader("Access-Control-Allow-Methods", query.allowMethods);
}
else {
if (query.responseHeaders) {
let responseHeaders = eval(query.responseHeaders);
for (let responseHeader in responseHeaders) {
response.setHeader(responseHeader, responseHeaders[responseHeader]);
}
}
if (query.exposeHeaders)
response.setHeader("Access-Control-Expose-Headers", query.exposeHeaders);
}
if (!isPreflight && query.hop && query.hop < hops.length) {
newURL = hops[query.hop].server +
"/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" +
"hop=" + (query.hop + 1) + "&hops=" + escape(query.hops);
if ("headers" in query) {
newURL += "&headers=" + escape(query.headers);
}
response.setStatusLine(null, 307, "redirect");
response.setHeader("Location", newURL);
return;
}
// Send response body
if (!isPreflight && request.method != "HEAD") {
response.setHeader("Content-Type", "application/xml", false);
response.write("<res>hello pass</res>\n");
}
if (isPreflight && "preflightBody" in query) {
response.setHeader("Content-Type", "text/plain", false);
response.write(query.preflightBody);
}
}
function sendHttp500(response, text) {
response.setStatusLine(null, 500, text);
}

View file

@ -0,0 +1,11 @@
[DEFAULT]
support-files =
file_CrossSiteXHR_cache_server.sjs
file_CrossSiteXHR_inner.html
file_CrossSiteXHR_inner.jar
file_CrossSiteXHR_inner_data.sjs
file_CrossSiteXHR_server.sjs
[test_CrossSiteXHR.html]
[test_CrossSiteXHR_cache.html]
[test_CrossSiteXHR_origin.html]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,587 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<title>Test for Cross Site XMLHttpRequest</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="gen.next()">
<p id="display">
<iframe id=loader></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("This test needs to generate artificial pauses, hence it uses timeouts. There is no way around it, unfortunately. :(");
window.addEventListener("message", function(e) {
gen.send(e.data);
}, false);
gen = runTest();
function runTest() {
var loader = document.getElementById('loader');
var loaderWindow = loader.contentWindow;
loader.onload = function () { gen.next() };
loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
origin = "http://example.org";
yield undefined;
tests = [{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue",
"y-my-header": "second" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "hello" },
allowHeaders: "y-my-header",
},
{ pass: 0,
method: "GET",
headers: { "y-my-header": "hello" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "hello" },
allowHeaders: "y-my-header,x-my-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue",
"y-my-header": "second" },
},
{ newTest: "*******" },
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 2
},
{ pause: 2.1 },
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header, y-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "z-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "\t 3600 \t ",
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "3600 3",
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: "asdf",
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "first-header": "myValue" },
allowHeaders: "first-header",
cacheTime: 2,
},
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
allowHeaders: "second-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "third-header": "myValue" },
allowHeaders: "third-header",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "first-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "first-header": "myValue" },
allowHeaders: "first-header",
cacheTime: 2,
},
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
allowHeaders: "second-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "third-header": "myValue" },
allowHeaders: "third-header",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 1,
method: "GET",
headers: { "second-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "third-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 0,
method: "DELETE",
},
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600
},
{ pass: 1,
method: "DELETE",
},
{ pass: 1,
method: "DELETE",
},
{ pass: 0,
method: "PATCH",
},
{ pass: 1,
method: "PATCH",
allowMethods: "PATCH",
},
{ pass: 0,
method: "PATCH",
},
{ pass: 1,
method: "PATCH",
allowMethods: "PATCH",
cacheTime: 3600,
},
{ pass: 1,
method: "PATCH",
},
{ pass: 0,
method: "DELETE",
},
{ pass: 0,
method: "PUT",
},
{ newTest: "*******" },
{ pass: 0,
method: "DELETE",
},
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 2
},
{ pause: 2.1 },
{ pass: 0,
method: "DELETE",
},
{ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE, PUT",
cacheTime: 3600
},
{ pass: 1,
method: "DELETE",
},
{ pass: 1,
method: "PUT",
},
{ pass: 0,
method: "PATCH",
},
{ newTest: "*******" },
{ pass: 1,
method: "FIRST",
allowMethods: "FIRST",
cacheTime: 2,
},
{ pass: 1,
method: "SECOND",
allowMethods: "SECOND",
cacheTime: 3600,
},
{ pass: 1,
method: "THIRD",
allowMethods: "THIRD",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 1,
method: "SECOND",
},
{ pass: 0,
method: "FIRST",
},
{ newTest: "*******" },
{ pass: 1,
method: "FIRST",
allowMethods: "FIRST",
cacheTime: 2,
},
{ pass: 1,
method: "SECOND",
allowMethods: "SECOND",
cacheTime: 3600,
},
{ pass: 1,
method: "THIRD",
allowMethods: "THIRD",
cacheTime: 2,
},
{ pause: 2.1 },
{ pass: 1,
method: "SECOND",
},
{ pass: 0,
method: "THIRD",
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" },
allowHeaders: "x-my-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" }
},
{ pass: 0,
method: "GET",
headers: { "y-my-header": "y-value" }
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "x-value" }
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" },
allowHeaders: "x-my-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" },
},
{ pass: 0,
method: "PUT",
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "x-value" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" },
allowHeaders: "x-my-header",
cacheTime: 3600,
},
{ pass: 1,
method: "GET",
headers: { "x-my-header": "x-value" },
},
{ pass: 0,
method: "GET",
noOrigin: 1,
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "x-value" },
},
{ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600,
},
{ pass: 1,
method: "DELETE"
},
{ pass: 0,
method: "PUT"
},
{ pass: 0,
method: "DELETE"
},
{ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600,
},
{ pass: 1,
method: "DELETE"
},
{ pass: 0,
method: "DELETE",
headers: { "my-header": "value" },
},
{ pass: 0,
method: "DELETE"
},
{ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600,
},
{ pass: 1,
method: "DELETE"
},
{ pass: 0,
method: "GET",
noOrigin: 1,
},
{ pass: 0,
method: "DELETE"
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
withCred: true,
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
withCred: true,
headers: { "x-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
withCred: true,
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 3600
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "myValue" },
allowHeaders: "y-my-header",
cacheTime: 2
},
{ pass: 1,
method: "GET",
headers: { "y-my-header": "myValue" },
},
{ pass: 1,
method: "GET",
withCred: true,
headers: { "x-my-header": "myValue" },
},
{ pause: 2.1 },
{ pass: 1,
method: "GET",
withCred: true,
headers: { "x-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "x-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
headers: { "y-my-header": "myValue" },
},
{ pass: 0,
method: "GET",
withCred: true,
headers: { "y-my-header": "myValue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600
},
{ pass: 0,
method: "GET",
headers: { "DELETE": "myvalue" },
},
{ newTest: "*******" },
{ pass: 1,
method: "GET",
headers: { "x-my-header": "myValue" },
allowHeaders: "x-my-header",
cacheTime: 3600
},
{ pass: 0,
method: "3600",
headers: { "x-my-header": "myvalue" },
},
];
for (let i = 0; i < 110; i++) {
tests.push({ newTest: "*******" },
{ pass: 1,
method: "DELETE",
allowMethods: "DELETE",
cacheTime: 3600,
});
}
baseURL = "http://mochi.test:8888/tests/dom/security/test/cors/" +
"file_CrossSiteXHR_cache_server.sjs?";
setStateURL = baseURL + "setState=";
var unique = Date.now();
for (test of tests) {
if (test.newTest) {
unique++;
continue;
}
if (test.pause) {
setTimeout(function() { gen.next() }, test.pause * 1000);
yield undefined;
continue;
}
req = {
url: baseURL + "c=" + unique,
method: test.method,
headers: test.headers,
withCred: test.withCred,
};
sec = { allowOrigin: test.noOrigin ? "" : origin,
allowHeaders: test.allowHeaders,
allowMethods: test.allowMethods,
cacheTime: test.cacheTime,
withCred: test.withCred };
xhr = new XMLHttpRequest();
xhr.open("POST", setStateURL + escape(sec.toSource()), true);
xhr.onloadend = function() { gen.next(); }
xhr.send();
yield undefined;
loaderWindow.postMessage(req.toSource(), origin);
res = eval(yield);
testName = test.toSource() + " (index " + tests.indexOf(test) + ")";
if (test.pass) {
is(res.didFail, false,
"shouldn't have failed in test for " + testName);
is(res.status, 200, "wrong status in test for " + testName);
is(res.responseXML, "<res>hello pass</res>",
"wrong responseXML in test for " + testName);
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + testName);
is(res.events.join(","),
"opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
"wrong events in test for " + testName);
}
else {
is(res.didFail, true,
"should have failed in test for " + testName);
is(res.status, 0, "wrong status in test for " + testName);
is(res.responseXML, null,
"wrong responseXML in test for " + testName);
is(res.responseText, "",
"wrong responseText in test for " + testName);
is(res.events.join(","),
"opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
"wrong events in test for " + testName);
is(res.progressEvents, 0,
"wrong events in test for " + testName);
}
}
SimpleTest.finish();
yield undefined;
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,174 @@
<!DOCTYPE HTML>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<title>Test for Cross Site XMLHttpRequest</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display">
<iframe id=loader></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
SimpleTest.requestLongerTimeout(2);
var origins =
[{ server: 'http://example.org' },
{ server: 'http://example.org:80',
origin: 'http://example.org'
},
{ server: 'http://sub1.test1.example.org' },
{ server: 'http://test2.example.org:8000' },
{ server: 'http://sub1.\xe4lt.example.org:8000',
origin: 'http://sub1.xn--lt-uia.example.org:8000'
},
{ server: 'http://sub2.\xe4lt.example.org',
origin: 'http://sub2.xn--lt-uia.example.org'
},
{ server: 'http://ex\xe4mple.test',
origin: 'http://xn--exmple-cua.test'
},
{ server: 'http://xn--exmple-cua.test' },
{ server: 'http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae',
origin: 'http://xn--hxajbheg2az3al.xn--jxalpdlp'
},
{ origin: 'http://example.org',
file: 'jar:http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.jar!/file_CrossSiteXHR_inner.html'
},
{ origin: 'null',
file: 'http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs'
},
];
//['https://example.com:443'],
//['https://sub1.test1.example.com:443'],
window.addEventListener("message", function(e) {
gen.send(e.data);
}, false);
gen = runTest();
function runTest() {
var loader = document.getElementById('loader');
var loaderWindow = loader.contentWindow;
loader.onload = function () { gen.next() };
// Test preflight-less requests
basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
baseURL = "http://mochi.test:8888" + basePath;
for (originEntry of origins) {
origin = originEntry.origin || originEntry.server;
loader.src = originEntry.file ||
(originEntry.server + "/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
yield undefined;
var isNullOrigin = origin == "null";
port = /:\d+/;
passTests = [
origin,
"*",
" \t " + origin + "\t \t",
"\t \t* \t ",
];
failTests = [
"",
" ",
port.test(origin) ? origin.replace(port, "")
: origin + ":1234",
port.test(origin) ? origin.replace(port, ":")
: origin + ":",
origin + ".",
origin + "/",
origin + "#",
origin + "?",
origin + "\\",
origin + "%",
origin + "@",
origin + "/hello",
"foo:bar@" + origin,
"* " + origin,
origin + " " + origin,
"allow <" + origin + ">",
"<" + origin + ">",
"<*>",
origin.substr(0, 5) == "https" ? origin.replace("https", "http")
: origin.replace("http", "https"),
origin.replace("://", "://www."),
origin.replace("://", ":// "),
origin.replace(/\/[^.]+\./, "/"),
];
if (isNullOrigin) {
passTests = ["*", "\t \t* \t ", "null"];
failTests = failTests.filter(function(v) { return v != origin });
}
for (allowOrigin of passTests) {
req = {
url: baseURL +
"allowOrigin=" + escape(allowOrigin) +
"&origin=" + escape(origin),
method: "GET",
};
loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin);
res = eval(yield);
is(res.didFail, false, "shouldn't have failed for " + allowOrigin);
is(res.status, 200, "wrong status for " + allowOrigin);
is(res.statusText, "OK", "wrong status text for " + allowOrigin);
is(res.responseXML,
"<res>hello pass</res>",
"wrong responseXML in test for " + allowOrigin);
is(res.responseText, "<res>hello pass</res>\n",
"wrong responseText in test for " + allowOrigin);
is(res.events.join(","),
"opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
"wrong responseText in test for " + allowOrigin);
}
for (allowOrigin of failTests) {
req = {
url: baseURL + "allowOrigin=" + escape(allowOrigin),
method: "GET",
};
loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin);
res = eval(yield);
is(res.didFail, true, "should have failed for " + allowOrigin);
is(res.responseText, "", "should have no text for " + allowOrigin);
is(res.status, 0, "should have no status for " + allowOrigin);
is(res.statusText, "", "wrong status text for " + allowOrigin);
is(res.responseXML, null, "should have no XML for " + allowOrigin);
is(res.events.join(","),
"opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
"wrong events in test for " + allowOrigin);
is(res.progressEvents, 0,
"wrong events in test for " + allowOrigin);
}
}
SimpleTest.finish();
yield undefined;
}
addLoadEvent(function() {
SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
gen.next();
});
});
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,13 @@
[DEFAULT]
support-files =
!/dom/security/test/csp/file_testserver.sjs
!/dom/security/test/csp/file_web_manifest.html
!/dom/security/test/csp/file_web_manifest.json
!/dom/security/test/csp/file_web_manifest.json^headers^
!/dom/security/test/csp/file_web_manifest_https.html
!/dom/security/test/csp/file_web_manifest_https.json
!/dom/security/test/csp/file_web_manifest_mixed_content.html
!/dom/security/test/csp/file_web_manifest_remote.html
[browser_test_web_manifest.js]
[browser_test_web_manifest_mixed_content.js]
[browser_manifest-src-override-default-src.js]

View file

@ -0,0 +1,108 @@
/*
* Description of the tests:
* Tests check that default-src can be overridden by manifest-src.
*/
/*globals Cu, is, ok*/
"use strict";
const {
ManifestObtainer
} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
const path = "/tests/dom/security/test/csp/";
const testFile = `${path}file_web_manifest.html`;
const mixedContentFile = `${path}file_web_manifest_mixed_content.html`;
const server = `${path}file_testserver.sjs`;
const defaultURL = new URL(`http://example.org${server}`);
const mixedURL = new URL(`http://mochi.test:8888${server}`);
const tests = [
// Check interaction with default-src and another origin,
// CSP allows fetching from example.org, so manifest should load.
{
expected: `CSP manifest-src overrides default-src of elsewhere.com`,
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("cors", "*");
url.searchParams.append("csp", "default-src http://elsewhere.com; manifest-src http://example.org");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
// Check interaction with default-src none,
// CSP allows fetching manifest from example.org, so manifest should load.
{
expected: `CSP manifest-src overrides default-src`,
get tabURL() {
const url = new URL(mixedURL);
url.searchParams.append("file", mixedContentFile);
url.searchParams.append("cors", "http://test:80");
url.searchParams.append("csp", "default-src 'self'; manifest-src http://test:80");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
];
//jscs:disable
add_task(function* () {
//jscs:enable
const testPromises = tests.map((test) => {
const tabOptions = {
gBrowser,
url: test.tabURL,
skipAnimation: true,
};
return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
});
yield Promise.all(testPromises);
});
function* testObtainingManifest(aBrowser, aTest) {
const expectsBlocked = aTest.expected.includes("block");
const observer = (expectsBlocked) ? createNetObserver(aTest) : null;
// Expect an exception (from promise rejection) if there a content policy
// that is violated.
try {
const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
aTest.run(manifest);
} catch (e) {
const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource");
ok(wasBlocked,`Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`);
if (observer) {
yield observer.untilFinished;
}
}
}
// Helper object used to observe policy violations. It waits 1 seconds
// for a response, and then times out causing its associated test to fail.
function createNetObserver(test) {
let finishedTest;
let success = false;
const finished = new Promise((resolver) => {
finishedTest = resolver;
});
const timeoutId = setTimeout(() => {
if (!success) {
test.run("This test timed out.");
finishedTest();
}
}, 1000);
var observer = {
get untilFinished(){
return finished;
},
observe(subject, topic) {
SpecialPowers.removeObserver(observer, "csp-on-violate-policy");
test.run(topic);
finishedTest();
clearTimeout(timeoutId);
success = true;
},
};
SpecialPowers.addObserver(observer, "csp-on-violate-policy", false);
return observer;
}

View file

@ -0,0 +1,224 @@
/*
* Description of the tests:
* These tests check for conformance to the CSP spec as they relate to Web Manifests.
*
* In particular, the tests check that default-src and manifest-src directives are
* are respected by the ManifestObtainer.
*/
/*globals Cu, is, ok*/
"use strict";
const {
ManifestObtainer
} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
const path = "/tests/dom/security/test/csp/";
const testFile = `${path}file_web_manifest.html`;
const remoteFile = `${path}file_web_manifest_remote.html`;
const httpsManifest = `${path}file_web_manifest_https.html`;
const server = `${path}file_testserver.sjs`;
const defaultURL = new URL(`http://example.org${server}`);
const secureURL = new URL(`https://example.com:443${server}`);
const tests = [
// CSP block everything, so trying to load a manifest
// will result in a policy violation.
{
expected: "default-src 'none' blocks fetching manifest.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src 'none'");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
}
},
// CSP allows fetching only from mochi.test:8888,
// so trying to load a manifest from same origin
// triggers a CSP violation.
{
expected: "default-src mochi.test:8888 blocks manifest fetching.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src mochi.test:8888");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
}
},
// CSP restricts fetching to 'self', so allowing the manifest
// to load. The name of the manifest is then checked.
{
expected: "CSP default-src 'self' allows fetch of manifest.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src 'self'");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
// CSP only allows fetching from mochi.test:8888 and remoteFile
// requests a manifest from that origin, so manifest should load.
{
expected: "CSP default-src mochi.test:8888 allows fetching manifest.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "default-src http://mochi.test:8888");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
// default-src blocks everything, so any attempt to
// fetch a manifest from another origin will trigger a
// policy violation.
{
expected: "default-src 'none' blocks mochi.test:8888",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "default-src 'none'");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
}
},
// CSP allows fetching from self, so manifest should load.
{
expected: "CSP manifest-src allows self",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src 'self'");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
// CSP allows fetching from example.org, so manifest should load.
{
expected: "CSP manifest-src allows http://example.org",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src http://example.org");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
}, {
expected: "CSP manifest-src allows mochi.test:8888",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("cors", "*");
url.searchParams.append("csp", "default-src *; manifest-src http://mochi.test:8888");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
// CSP restricts fetching to mochi.test:8888, but the test
// file is at example.org. Hence, a policy violation is
// triggered.
{
expected: "CSP blocks manifest fetching from example.org.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src mochi.test:8888");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
}
},
// CSP is set to only allow manifest to be loaded from same origin,
// but the remote file attempts to load from a different origin. Thus
// this causes a CSP violation.
{
expected: "CSP manifest-src 'self' blocks cross-origin fetch.",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "manifest-src 'self'");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
}
},
// CSP allows fetching over TLS from example.org, so manifest should load.
{
expected: "CSP manifest-src allows example.com over TLS",
get tabURL() {
// secureURL loads https://example.com:443
// and gets manifest from https://example.org:443
const url = new URL(secureURL);
url.searchParams.append("file", httpsManifest);
url.searchParams.append("cors", "*");
url.searchParams.append("csp", "manifest-src https://example.com:443");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
}
},
];
//jscs:disable
add_task(function* () {
//jscs:enable
const testPromises = tests.map((test) => {
const tabOptions = {
gBrowser,
url: test.tabURL,
skipAnimation: true,
};
return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
});
yield Promise.all(testPromises);
});
function* testObtainingManifest(aBrowser, aTest) {
const waitForObserver = waitForNetObserver(aTest);
// Expect an exception (from promise rejection) if there a content policy
// that is violated.
try {
const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
aTest.run(manifest);
} catch (e) {
const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource");
ok(wasBlocked, `Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`);
} finally {
yield waitForObserver;
}
}
// Helper object used to observe policy violations when blocking is expected.
function waitForNetObserver(aTest) {
return new Promise((resolve) => {
// We don't need to wait for violation, so just resolve
if (!aTest.expected.includes("block")){
return resolve();
}
const observer = {
observe(subject, topic) {
SpecialPowers.removeObserver(observer, "csp-on-violate-policy");
aTest.run(topic);
resolve();
},
};
SpecialPowers.addObserver(observer, "csp-on-violate-policy", false);
});
}

View file

@ -0,0 +1,53 @@
/*
* Description of the test:
* Check that mixed content blocker works prevents fetches of
* mixed content manifests.
*/
/*globals Cu, ok*/
"use strict";
const {
ManifestObtainer
} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
const path = "/tests/dom/security/test/csp/";
const mixedContent = `${path}file_web_manifest_mixed_content.html`;
const server = `${path}file_testserver.sjs`;
const secureURL = new URL(`https://example.com${server}`);
const tests = [
// Trying to load mixed content in file_web_manifest_mixed_content.html
// needs to result in an error.
{
expected: "Mixed Content Blocker prevents fetching manifest.",
get tabURL() {
const url = new URL(secureURL);
url.searchParams.append("file", mixedContent);
return url.href;
},
run(error) {
// Check reason for error.
const check = /NetworkError when attempting to fetch resource/.test(error.message);
ok(check, this.expected);
}
}
];
//jscs:disable
add_task(function* () {
//jscs:enable
const testPromises = tests.map((test) => {
const tabOptions = {
gBrowser,
url: test.tabURL,
skipAnimation: true,
};
return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
});
yield Promise.all(testPromises);
});
function* testObtainingManifest(aBrowser, aTest) {
try {
yield ManifestObtainer.browserObtainManifest(aBrowser);
} catch (e) {
aTest.run(e);
}
}

View file

@ -0,0 +1,20 @@
/*
* Moved this CSS from an inline stylesheet to an external file when we added
* inline-style blocking in bug 763879.
* This test may hang if the load for this .css file is blocked due to a
* malfunction of CSP, but should pass if the style_good test passes.
*/
/* CSS font embedding tests */
@font-face {
font-family: "arbitrary_good";
src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
}
@font-face {
font-family: "arbitrary_bad";
src: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
}
.div_arbitrary_good { font-family: "arbitrary_good"; }
.div_arbitrary_bad { font-family: "arbitrary_bad"; }

View file

@ -0,0 +1,26 @@
// SJS file for CSP mochitests
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
var isPreflight = request.method == "OPTIONS";
//avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
if ("type" in query) {
response.setHeader("Content-Type", unescape(query['type']), false);
} else {
response.setHeader("Content-Type", "text/html", false);
}
if ("content" in query) {
response.write(unescape(query['content']));
}
}

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 826805 - CSP: Allow http and https for scheme-less sources</title>
</head>
<body>
<div id="testdiv">blocked</div>
<!--
We resue file_path_matching.js which just updates the contents of 'testdiv' to contain allowed.
Note, that we are loading the file_path_matchting.js using a scheme of 'https'.
-->
<script src="https://example.com/tests/dom/security/test/csp/file_path_matching.js#foo"></script>
</body>
</html>

View file

@ -0,0 +1,61 @@
// Custom *.sjs file specifically for the needs of
// https://bugzilla.mozilla.org/show_bug.cgi?id=1263286
"use strict";
Components.utils.importGlobalProperties(["URLSearchParams"]);
const PRE_BASE = `
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045897 - Test CSP base-uri directive</title>`;
const REGULAR_POST_BASE =`
</head>
<body onload='window.parent.postMessage({result: document.baseURI}, "*");'>
<!-- just making use of the 'base' tag for this test -->
</body>
</html>`;
const SCRIPT_POST_BASE = `
</head>
<body>
<script>
document.getElementById("base1").removeAttribute("href");
window.parent.postMessage({result: document.baseURI}, "*");
</script>
</body>
</html>`;
function handleRequest(request, response) {
const query = new URLSearchParams(request.queryString);
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
// Deliver the CSP policy encoded in the URL
response.setHeader("Content-Security-Policy", query.get("csp"), false);
// Send HTML to test allowed/blocked behaviors
response.setHeader("Content-Type", "text/html", false);
response.write(PRE_BASE);
var base1 =
"<base id=\"base1\" href=\"" + query.get("base1") + "\">";
var base2 =
"<base id=\"base2\" href=\"" + query.get("base2") + "\">";
response.write(base1 + base2);
if (query.get("action") === "enforce-csp") {
response.write(REGULAR_POST_BASE);
return;
}
if (query.get("action") === "remove-base1") {
response.write(SCRIPT_POST_BASE);
return;
}
// we should never get here, but just in case
// return something unexpected
response.write("do'h");
}

View file

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1086999 - Wildcard should not match blob:, data:</title>
</head>
<body>
<script type="text/javascript">
var base64data =
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
"P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
// construct an image element using *data:*
var data_src = "data:image/png;base64," + base64data;
var data_img = document.createElement('img');
data_img.onload = function() {
window.parent.postMessage({scheme: "data", result: "allowed"}, "*");
}
data_img.onerror = function() {
window.parent.postMessage({scheme: "data", result: "blocked"}, "*");
}
data_img.src = data_src;
document.body.appendChild(data_img);
// construct an image element using *blob:*
var byteCharacters = atob(base64data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray], {type: "image/png"});
var imageUrl = URL.createObjectURL( blob );
var blob_img = document.createElement('img');
blob_img.onload = function() {
window.parent.postMessage({scheme: "blob", result: "allowed"}, "*");
}
blob_img.onerror = function() {
window.parent.postMessage({scheme: "blob", result: "blocked"}, "*");
}
blob_img.src = imageUrl;
document.body.appendChild(blob_img);
</script>
</body>
</html>

View file

@ -0,0 +1,76 @@
// custom *.sjs for Bug 1122236
// CSP: 'block-all-mixed-content'
const HEAD =
"<!DOCTYPE HTML>" +
"<html><head><meta charset=\"utf-8\">" +
"<title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>" +
"</head>";
const CSP_ALLOW =
"<meta http-equiv=\"Content-Security-Policy\" content=\"img-src *\">";
const CSP_BLOCK =
"<meta http-equiv=\"Content-Security-Policy\" content=\"block-all-mixed-content\">";
const BODY =
"<body>" +
"<img id=\"testimage\" src=\"http://mochi.test:8888/tests/image/test/mochitest/blue.png\"></img>" +
"<script type=\"application/javascript\">" +
" var myImg = document.getElementById(\"testimage\");" +
" myImg.onload = function(e) {" +
" window.parent.postMessage({result: \"img-loaded\"}, \"*\");" +
" };" +
" myImg.onerror = function(e) {" +
" window.parent.postMessage({result: \"img-blocked\"}, \"*\");" +
" };" +
"</script>" +
"</body>" +
"</html>";
// We have to use this special code fragment, in particular '?nocache' to trigger an
// actual network load rather than loading the image from the cache.
const BODY_CSPRO =
"<body>" +
"<img id=\"testimage\" src=\"http://mochi.test:8888/tests/image/test/mochitest/blue.png?nocache\"></img>" +
"<script type=\"application/javascript\">" +
" var myImg = document.getElementById(\"testimage\");" +
" myImg.onload = function(e) {" +
" window.parent.postMessage({result: \"img-loaded\"}, \"*\");" +
" };" +
" myImg.onerror = function(e) {" +
" window.parent.postMessage({result: \"img-blocked\"}, \"*\");" +
" };" +
"</script>" +
"</body>" +
"</html>";
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
var queryString = request.queryString;
if (queryString === "csp-block") {
response.write(HEAD + CSP_BLOCK + BODY);
return;
}
if (queryString === "csp-allow") {
response.write(HEAD + CSP_ALLOW + BODY);
return;
}
if (queryString === "no-csp") {
response.write(HEAD + BODY);
return;
}
if (queryString === "cspro-block") {
// CSP RO is not supported in meta tag, let's use the header
response.setHeader("Content-Security-Policy-Report-Only", "block-all-mixed-content", false);
response.write(HEAD + BODY_CSPRO);
return;
}
// we should never get here but just in case return something unexpected
response.write("do'h");
}

View file

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
<title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
</head>
<body>
<b>user clicks and navigates from https://b.com to http://c.com</b>
<a id="navlink" href="http://example.com/tests/dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html">foo</a>
<script class="testbody" type="text/javascript">
// click the link to start the frame navigation
document.getElementById("navlink").click();
</script>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
</head>
<body>
<b>http://c.com loaded, let's tell the parent</b>
<script class="testbody" type="text/javascript">
window.parent.postMessage({result: "frame-navigated"}, "*");
</script>
</body>
</html>

View file

@ -0,0 +1,7 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- this should be allowed -->
<script src="http://mochi.test:8888/tests/dom/security/test/csp/%24.js"> </script>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: "default-src 'self'; script-src http://mochi.test:8888/tests/dom/security/test/csp/%24.js

View file

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<meta charset="utf-8">
<title>marquee inline script tests for Bug 1312272</title>
</head>
<body>
<marquee id="m" onstart="parent.postMessage('csp-violation-marquee-onstart', '*')">bug 1312272</marquee>
<script src="file_bug1312272.js"></script>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src *; script-src * 'unsafe-eval'

View file

@ -0,0 +1,8 @@
var m = document.getElementById("m");
m.addEventListener("click", function() {
// this will trigger after onstart, obviously.
parent.postMessage('finish', '*');
});
console.log("finish-handler setup");
m.click();
console.log("clicked");

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2 id="xsltheader">this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!</h2>
<table border="1">
<tr bgcolor="#990099">
<th>Title</th>
<th>Artist</th>
<th>Price</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="file_bug663567.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="file_bug663567.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src *.example.com

View file

@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 802872</title>
<!-- Including SimpleTest.js so we can use AddLoadEvent !-->
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script src='file_bug802872.js'></script>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View file

@ -0,0 +1,43 @@
/*
* The policy for this test is:
* Content-Security-Policy: default-src 'self'
*/
function createAllowedEvent() {
/*
* Creates a new EventSource using 'http://mochi.test:8888'. Since all mochitests run on
* 'http://mochi.test', a default-src of 'self' allows this request.
*/
var src_event = new EventSource("http://mochi.test:8888/tests/dom/security/test/csp/file_bug802872.sjs");
src_event.onmessage = function(e) {
src_event.close();
parent.dispatchEvent(new Event('allowedEventSrcCallbackOK'));
}
src_event.onerror = function(e) {
src_event.close();
parent.dispatchEvent(new Event('allowedEventSrcCallbackFailed'));
}
}
function createBlockedEvent() {
/*
* creates a new EventSource using 'http://example.com'. This domain is not whitelisted by the
* CSP of this page, therefore the CSP blocks this request.
*/
var src_event = new EventSource("http://example.com/tests/dom/security/test/csp/file_bug802872.sjs");
src_event.onmessage = function(e) {
src_event.close();
parent.dispatchEvent(new Event('blockedEventSrcCallbackOK'));
}
src_event.onerror = function(e) {
src_event.close();
parent.dispatchEvent(new Event('blockedEventSrcCallbackFailed'));
}
}
addLoadEvent(createAllowedEvent);
addLoadEvent(createBlockedEvent);

View file

@ -0,0 +1,7 @@
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/event-stream", false);
response.write("data: eventsource response from server!");
response.write("\n\n");
}

View file

@ -0,0 +1,12 @@
<html>
<head>
<link rel='stylesheet' type='text/css'
href='/tests/dom/security/test/csp/file_CSP.sjs?testid=css_self&type=text/css' />
</head>
<body>
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img_self&type=img/png"> </img>
<script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script_self&type=text/javascript'></script>
</body>
</html>

View file

@ -0,0 +1,2 @@
content-security-policy: default-src 'self'; img-src 'none'; report-uri http://mochi.test:8888/tests/dom/security/test/csp/file_bug836922_npolicies_violation.sjs
content-security-policy-report-only: default-src *; img-src 'self'; script-src 'none'; report-uri http://mochi.test:8888/tests/dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs

View file

@ -0,0 +1,53 @@
// SJS file that receives violation reports and then responds with nothing.
const CC = Components.Constructor;
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
const STATE_KEY = "bug836922_ro_violations";
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
if ('results' in query) {
// if asked for the received data, send it.
response.setHeader("Content-Type", "text/javascript", false);
if (getState(STATE_KEY)) {
response.write(getState(STATE_KEY));
} else {
// no state has been recorded.
response.write(JSON.stringify({}));
}
} else if ('reset' in query) {
//clear state
setState(STATE_KEY, JSON.stringify(null));
} else {
// ... otherwise, just respond "ok".
response.write("null");
var bodystream = new BinaryInputStream(request.bodyInputStream);
var avail;
var bytes = [];
while ((avail = bodystream.available()) > 0)
Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
var data = String.fromCharCode.apply(null, bytes);
// figure out which test was violating a policy
var testpat = new RegExp("testid=([a-z0-9_]+)");
var testid = testpat.exec(data)[1];
// store the violation in the persistent state
var s = JSON.parse(getState(STATE_KEY) || "{}");
s[testid] ? s[testid]++ : s[testid] = 1;
setState(STATE_KEY, JSON.stringify(s));
}
}

View file

@ -0,0 +1,59 @@
// SJS file that receives violation reports and then responds with nothing.
const CC = Components.Constructor;
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream",
"setInputStream");
const STATE = "bug836922_violations";
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
if ('results' in query) {
// if asked for the received data, send it.
response.setHeader("Content-Type", "text/javascript", false);
if (getState(STATE)) {
response.write(getState(STATE));
} else {
// no state has been recorded.
response.write(JSON.stringify({}));
}
} else if ('reset' in query) {
//clear state
setState(STATE, JSON.stringify(null));
} else {
// ... otherwise, just respond "ok".
response.write("null");
var bodystream = new BinaryInputStream(request.bodyInputStream);
var avail;
var bytes = [];
while ((avail = bodystream.available()) > 0)
Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
var data = String.fromCharCode.apply(null, bytes);
// figure out which test was violating a policy
var testpat = new RegExp("testid=([a-z0-9_]+)");
var testid = testpat.exec(data)[1];
// store the violation in the persistent state
var s = getState(STATE);
if (!s) s = "{}";
s = JSON.parse(s);
if (!s) s = {};
if (!s[testid]) s[testid] = 0;
s[testid]++;
setState(STATE, JSON.stringify(s));
}
}

View file

@ -0,0 +1,38 @@
<!doctype html>
<!--
The Content-Security-Policy header for this file is:
Content-Security-Policy: img-src 'self';
It does not include any of the default-src, script-src, or style-src
directives. It should allow the use of unsafe-inline and unsafe-eval on
scripts, and unsafe-inline on styles, because no directives related to scripts
or styles are specified.
-->
<html>
<body>
<ol>
<li id="unsafe-inline-script-allowed">Inline script allowed (this text should be green)</li>
<li id="unsafe-eval-script-allowed">Eval script allowed (this text should be green)</li>
<li id="unsafe-inline-style-allowed">Inline style allowed (this text should be green)</li>
</ol>
<script>
// Use inline script to set a style attribute
document.getElementById("unsafe-inline-script-allowed").style.color = "green";
// Use eval to set a style attribute
// try/catch is used because CSP causes eval to throw an exception when it
// is blocked, which would derail the rest of the tests in this file.
try {
eval('document.getElementById("unsafe-eval-script-allowed").style.color = "green";');
} catch (e) {}
</script>
<style>
li#unsafe-inline-style-allowed {
color: green;
}
</style>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: img-src 'self';

View file

@ -0,0 +1,37 @@
<!doctype html>
<!--
The Content-Security-Policy header for this file is:
Content-Security-Policy: default-src 'self';
The Content-Security-Policy header for this file includes the default-src
directive, which triggers the default behavior of blocking unsafe-inline and
unsafe-eval on scripts, and unsafe-inline on styles.
-->
<html>
<body>
<ol>
<li id="unsafe-inline-script-blocked">Inline script blocked (this text should be black)</li>
<li id="unsafe-eval-script-blocked">Eval script blocked (this text should be black)</li>
<li id="unsafe-inline-style-blocked">Inline style blocked (this text should be black)</li>
</ol>
<script>
// Use inline script to set a style attribute
document.getElementById("unsafe-inline-script-blocked").style.color = "green";
// Use eval to set a style attribute
// try/catch is used because CSP causes eval to throw an exception when it
// is blocked, which would derail the rest of the tests in this file.
try {
eval('document.getElementById("unsafe-eval-script-blocked").style.color = "green";');
} catch (e) {}
</script>
<style>
li#unsafe-inline-style-blocked {
color: green;
}
</style>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self';

View file

@ -0,0 +1,15 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox="allow-same-origin" -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
<script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View file

@ -0,0 +1,14 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'self' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
<!-- these should load ok -->
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self'

View file

@ -0,0 +1,12 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none'

View file

@ -0,0 +1,12 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- sandbox -->
<!-- Content-Security-Policy: default-src 'none' -->
<!-- these should be stopped by CSP -->
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none'

View file

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html>
<head> <meta charset="utf-8"> </head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with only inline "allow-scripts"
<!-- sandbox="allow-scripts" -->
<!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
<!-- these should be stopped by CSP -->
<img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
<script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
<script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none' 'unsafe-inline';

View file

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<script type="text/javascript">
function ok(result, desc) {
window.parent.postMessage({ok: result, desc: desc}, "*");
}
function doStuff() {
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
document.getElementById('a_form').submit();
// trigger the javascript: url test
sendMouseEvent({type:'click'}, 'a_link');
}
</script>
<script src='file_iframe_sandbox_pass.js'></script>
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
I am sandboxed but with "allow-scripts"
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
<script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
<form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
First name: <input type="text" name="firstname">
Last name: <input type="text" name="lastname">
<input type="submit" onclick="doSubmit()" id="a_button">
</form>
<a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self' 'unsafe-inline';

View file

@ -0,0 +1,28 @@
<!doctype html>
<html>
<body>
<ol>
<li id="unsafe-inline-script">Inline script (green if allowed, black if blocked)</li>
<li id="unsafe-eval-script">Eval script (green if allowed, black if blocked)</li>
<li id="unsafe-inline-style">Inline style (green if allowed, black if blocked)</li>
</ol>
<script>
// Use inline script to set a style attribute
document.getElementById("unsafe-inline-script").style.color = "green";
// Use eval to set a style attribute
// try/catch is used because CSP causes eval to throw an exception when it
// is blocked, which would derail the rest of the tests in this file.
try {
eval('document.getElementById("unsafe-eval-script").style.color = "green";');
} catch (e) {}
</script>
<style>
li#unsafe-inline-style {
color: green;
}
</style>
</body>
</html>

View file

@ -0,0 +1,43 @@
// SJS file for CSP mochitests
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function loadHTMLFromFile(path) {
// Load the HTML to return in the response from file.
// 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.
var testHTMLFile =
Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var dirs = path.split("/");
for (var i = 0; i < dirs.length; i++) {
testHTMLFile.append(dirs[i]);
}
var testHTMLFileStream =
Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
return testHTML;
}
function handleRequest(request, response)
{
var query = {};
request.queryString.split('&').forEach(function (val) {
var [name, value] = val.split('=');
query[name] = unescape(value);
});
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
// Deliver the CSP policy encoded in the URI
if (query['csp'])
response.setHeader("Content-Security-Policy", unescape(query['csp']), false);
// Send HTML to test allowed/blocked behaviors
response.setHeader("Content-Type", "text/html", false);
response.write(loadHTMLFromFile("tests/dom/security/test/csp/file_bug888172.html"));
}

View file

@ -0,0 +1,20 @@
<!doctype html>
<html>
<head>
<!-- file_CSP.sjs mocks a resource load -->
<link rel='stylesheet' type='text/css'
href='file_CSP.sjs?testid=noneExternalStylesBlocked&type=text/css' />
</head>
<body>
<p id="inline-style">This should be green</p>
<p id="inline-script">This should be black</p>
<style>
p#inline-style { color:rgb(0, 128, 0); }
</style>
<script>
// Use inline script to set a style attribute
document.getElementById("inline-script").style.color = "rgb(0, 128, 0)";
</script>
<img src="file_CSP.sjs?testid=noneExternalImgLoaded&type=img/png" />
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src * ; style-src 'none' 'unsafe-inline';

View file

@ -0,0 +1,19 @@
<!doctype html>
<html>
<head>
<link rel='stylesheet' type='text/css'
href='file_CSP.sjs?testid=starExternalStylesLoaded&type=text/css' />
</head>
<body>
<p id="inline-style">This should be green</p>
<p id="inline-script">This should be black</p>
<style>
p#inline-style { color:rgb(0, 128, 0); }
</style>
<script>
// Use inline script to set a style attribute
document.getElementById("inline-script").style.color = "rgb(0, 128, 0)";
</script>
<img src="file_CSP.sjs?testid=starExternalImgLoaded&type=img/png" />
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src *; style-src * 'unsafe-inline';

View file

@ -0,0 +1,52 @@
// Server side js file for bug 910139, see file test_bug910139.html for details.
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function loadResponseFromFile(path) {
var testHTMLFile =
Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var dirs = path.split("/");
for (var i = 0; i < dirs.length; i++) {
testHTMLFile.append(dirs[i]);
}
var testHTMLFileStream =
Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
return testHTML;
}
var policies = [
"default-src 'self'; script-src 'self'", // CSP for checkAllowed
"default-src 'self'; script-src *.example.com" // CSP for checkBlocked
]
function getPolicy() {
var index;
// setState only accepts strings as arguments
if (!getState("counter")) {
index = 0;
setState("counter", index.toString());
}
else {
index = parseInt(getState("counter"));
++index;
setState("counter", index.toString());
}
return policies[index];
}
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
// set the required CSP
response.setHeader("Content-Security-Policy", getPolicy(), false);
// return the requested XML file.
response.write(loadResponseFromFile("tests/dom/security/test/csp/file_bug910139.xml"));
}

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="file_bug910139.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<country>USA</country>
<company>RCA</company>
<price>9.90</price>
<year>1982</year>
</cd>
</catalog>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2 id="xsltheader">this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!</h2>
<table border="1">
<tr bgcolor="#990099">
<th>Title</th>
<th>Artist</th>
<th>Price</th>
</tr>
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="artist"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View file

@ -0,0 +1,27 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
<!-- this should be allowed (no CSP)-->
<img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_good&type=img/png"> </img>
<script type="text/javascript">
var req = new XMLHttpRequest();
req.onload = function() {
//this should be allowed (no CSP)
try {
var img = document.createElement("img");
img.src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_good&type=img/png";
document.body.appendChild(img);
} catch(e) {
console.log("yo: "+e);
}
};
req.open("get", "file_bug941404_xhr.html", true);
req.responseType = "document";
req.send();
</script>
</body>
</html>

View file

@ -0,0 +1,5 @@
<html>
<head> <meta charset="utf-8"> </head>
<body>
</body>
</html>

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none' 'unsafe-inline' 'unsafe-eval'

View file

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<iframe id="testframe"> </iframe>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
function executeTest(ev) {
testframe = document.getElementById('testframe');
testframe.contentWindow.postMessage({id:page_id, message:"execute"}, 'http://mochi.test:8888');
}
function reportError(ev) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
cleanup();
}
function recvMessage(ev) {
if (ev.data.id == page_id) {
window.parent.postMessage({id:ev.data.id, message:ev.data.message}, 'http://mochi.test:8888');
cleanup();
}
}
function cleanup() {
testframe = document.getElementById('testframe');
window.removeEventListener('message', recvMessage);
testframe.removeEventListener('load', executeTest);
testframe.removeEventListener('error', reportError);
}
window.addEventListener('message', recvMessage, false);
try {
// Please note that file_testserver.sjs?foo does not return a response.
// For testing purposes this is not necessary because we only want to check
// whether CSP allows or blocks the load.
src = "file_testserver.sjs";
src += "?file=" + escape("tests/dom/security/test/csp/file_child-src_inner_frame.html");
src += "#" + escape(page_id);
testframe = document.getElementById('testframe');
testframe.addEventListener('load', executeTest, false);
testframe.addEventListener('error', reportError, false);
testframe.src = src;
}
catch (e) {
if (e.message.match(/Failed to load script/)) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
} else {
window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<iframe id="innermosttestframe"> </iframe>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
function recvMessage(ev) {
if (ev.data.id == page_id) {
window.parent.postMessage({id:ev.data.id, message:'allowed'}, 'http://mochi.test:8888');
window.removeEventListener('message', recvMessage);
}
}
window.addEventListener('message', recvMessage, false);
</script>
</body>
</html>

View file

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
try {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(
'file_child-src_service_worker.js',
{ scope: './' + page_id + '/' }
).then(function(reg)
{
// registration worked
reg.unregister().then(function() {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
});
}).catch(function(error) {
// registration failed
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
});
};
} catch(ex) {
window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
}
</script>
</body>
</html>

View file

@ -0,0 +1,3 @@
this.addEventListener('install', function(event) {
close();
});

View file

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
var redir = 'none';
page_id.split('_').forEach(function (val) {
var [name, value] = val.split('-');
if (name == 'redir') {
redir = unescape(value);
}
});
try {
worker = new SharedWorker('file_redirect_worker.sjs?path='
+ escape("/tests/dom/security/test/csp/file_child-src_shared_worker.js")
+ "&redir=" + redir
+ "&page_id=" + page_id,
page_id);
worker.port.start();
worker.onerror = function(evt) {
evt.preventDefault();
window.parent.postMessage({id:page_id, message:"blocked"},
'http://mochi.test:8888');
}
worker.port.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
};
worker.onerror = function() {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
};
worker.port.postMessage('foo');
}
catch (e) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
}
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
try {
worker = new SharedWorker(
'file_testserver.sjs?file='+
escape("tests/dom/security/test/csp/file_child-src_shared_worker.js"),
page_id);
worker.port.start();
worker.onerror = function(evt) {
evt.preventDefault();
window.parent.postMessage({id:page_id, message:"blocked"},
'http://mochi.test:8888');
}
worker.port.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"},
'http://mochi.test:8888');
};
worker.port.postMessage('foo');
}
catch (e) {
window.parent.postMessage({id:page_id, message:"blocked"},
'http://mochi.test:8888');
}
</script>
</body>
</html>

View file

@ -0,0 +1,8 @@
onconnect = function(e) {
var port = e.ports[0];
port.addEventListener('message', function(e) {
port.postMessage('success');
});
port.start();
}

View file

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
var page_id = window.location.hash.substring(1);
var shared_worker = "onconnect = function(e) { " +
"var port = e.ports[0];" +
"port.addEventListener('message'," +
"function(e) { port.postMessage('success'); });" +
"port.start(); }";
try {
var worker = new SharedWorker('data:application/javascript;charset=UTF-8,'+
escape(shared_worker), page_id);
worker.port.start();
worker.onerror = function(evt) {
evt.preventDefault();
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
}
worker.port.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
};
worker.port.postMessage('foo');
}
catch (e) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
}
</script>
</body>
</html>

View file

@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
var page_id = window.location.hash.substring(1);
var redir = 'none';
page_id.split('_').forEach(function (val) {
var [name, value] = val.split('-');
if (name == 'redir') {
redir = unescape(value);
}
});
try {
worker = new Worker('file_redirect_worker.sjs?path='
+ escape("/tests/dom/security/test/csp/file_child-src_worker.js")
+ "&redir=" + redir
+ "&page_id=" + page_id
);
worker.onerror = function(error) {
var msg = error.message;
if (msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/)) {
// this means CSP blocked it
msg = "blocked";
}
window.parent.postMessage({id:page_id, message:msg}, 'http://mochi.test:8888');
error.preventDefault();
};
worker.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
};
worker.postMessage('foo');
}
catch (e) {
if (e.message.match(/Failed to load script/)) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
} else {
window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
try {
worker = new Worker('file_testserver.sjs?file='+escape("tests/dom/security/test/csp/file_child-src_worker.js"));
worker.onerror = function(e) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
e.preventDefault();
}
worker.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
}
worker.postMessage('foo');
}
catch (e) {
if (e.message.match(/Failed to load script/)) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
} else {
window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,4 @@
onmessage = function(e) {
postMessage('worker');
};

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1045891</title>
</head>
<body>
<script type="text/javascript">
page_id = window.location.hash.substring(1);
try {
worker = new Worker('data:application/javascript;charset=UTF-8,'+escape('onmessage = function(e) { postMessage("worker"); };'));
worker.onerror = function(e) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
e.preventDefault();
}
worker.onmessage = function(ev) {
window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
}
worker.postMessage('foo');
}
catch (e) {
if (e.message.match(/Failed to load script/)) {
window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
} else {
console.log(e);
window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,39 @@
function doXHR(uri) {
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", uri);
xhr.send();
} catch(ex) {}
}
var sameBase = "http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=";
var crossBase = "http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=";
onmessage = (e) => {
for (base of [sameBase, crossBase]) {
var prefix;
var suffix;
if (e.data.inherited == "parent") {
//Worker inherits CSP from parent worker
prefix = base + "worker_child_inherited_parent_";
suffix = base == sameBase ? "_good" : "_bad";
} else if (e.data.inherited == "document") {
//Worker inherits CSP from owner document -> parent worker -> subworker
prefix = base + "worker_child_inherited_document_";
suffix = base == sameBase ? "_good" : "_bad";
} else {
// Worker delivers CSP from HTTP header
prefix = base + "worker_child_";
suffix = base == sameBase ? "_same_bad" : "_cross_bad";
}
doXHR(prefix + "xhr" + suffix);
// Fetch is likely failed in subworker
// See Bug 1273070 - Failed to fetch in subworker
// Enable fetch test after the bug is fixed
// fetch(prefix + "xhr" + suffix);
try {
importScripts(prefix + "script" + suffix);
} catch(ex) {}
}
}

View file

@ -0,0 +1 @@
Content-Security-Policy: default-src 'none'

Some files were not shown because too many files have changed in this diff Show more