1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/test/csp/test_csp_bug773891.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,228 @@ 1.4 +<!DOCTYPE HTML> 1.5 +<html> 1.6 +<!-- 1.7 + https://bugzilla.mozilla.org/show_bug.cgi?id=768029 1.8 +--> 1.9 +<head> 1.10 + <meta charset="utf-8"> 1.11 + <title>Test for CSP on trusted/certified and installed apps -- bug 773891</title> 1.12 + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 1.13 + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> 1.14 +</head> 1.15 +<body> 1.16 +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=773891">Mozilla Bug 773891</a> 1.17 +<p id="display"></p> 1.18 +<div id="content"> 1.19 + 1.20 +</div> 1.21 +<pre id="test"> 1.22 +<script type="application/javascript;version=1.7"> 1.23 + 1.24 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.25 + 1.26 +/** Test for Bug 773891 **/ 1.27 + 1.28 +// Note: we don't have to inspect all the different operations of CSP, 1.29 +// we're just looking for specific differences in behavior that indicate 1.30 +// a default CSP got applied. 1.31 +const DEFAULT_CSP_PRIV = "default-src *; script-src *; style-src 'self' 'unsafe-inline'; object-src 'none'"; 1.32 +const DEFAULT_CSP_CERT = "default-src *; script-src *; style-src 'self'; object-src 'none'"; 1.33 + 1.34 +const MANIFEST_CSP_PRIV = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"; 1.35 +const MANIFEST_CSP_INST = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"; 1.36 +const MANIFEST_CSP_CERT = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"; 1.37 + 1.38 +SimpleTest.waitForExplicitFinish(); 1.39 + 1.40 +var gData = [ 1.41 + 1.42 + { 1.43 + app: "https://example.com/manifest_csp_inst.webapp", 1.44 + appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED, 1.45 + csp: MANIFEST_CSP_INST, 1.46 + origin: "https://example.com", 1.47 + uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html", 1.48 + statusString: "installed app", 1.49 + expectedTestResults: { 1.50 + max_tests: 7, /* number of bools below plus one for the status check */ 1.51 + cross_origin: { img: true, script: false, style: false }, 1.52 + same_origin: { img: true, script: true, style: true }, 1.53 + }, 1.54 + }, 1.55 + { 1.56 + app: "https://example.com/manifest_csp_cert.webapp", 1.57 + appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED, 1.58 + csp: MANIFEST_CSP_CERT, 1.59 + origin: "https://example.com", 1.60 + uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html", 1.61 + statusString: "certified app", 1.62 + expectedTestResults: { 1.63 + max_tests: 7, /* number of bools below plus one for the status check */ 1.64 + cross_origin: { img: true, script: false, style: false }, 1.65 + same_origin: { img: true, script: true, style: true }, 1.66 + }, 1.67 + }, 1.68 + { 1.69 + app: "https://example.com/manifest_csp_priv.webapp", 1.70 + appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED, 1.71 + csp: MANIFEST_CSP_PRIV, 1.72 + origin: "https://example.com", 1.73 + uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html", 1.74 + statusString: "privileged app", 1.75 + expectedTestResults: { 1.76 + max_tests: 7, /* number of bools below plus one for the status check */ 1.77 + cross_origin: { img: true, script: false, style: false }, 1.78 + same_origin: { img: true, script: true, style: true }, 1.79 + }, 1.80 + }, 1.81 +]; 1.82 + 1.83 +// Observer for watching allowed loads and blocked attempts 1.84 +function ThingyListener(app, iframe) { 1.85 + Services.obs.addObserver(this, "csp-on-violate-policy", false); 1.86 + Services.obs.addObserver(this, "http-on-modify-request", false); 1.87 + dump("added observers\n"); 1.88 + // keep track of which app ID this test is monitoring. 1.89 + this._testData = app; 1.90 + this._expectedResults = app.expectedTestResults; 1.91 + this._resultsRecorded = { cross_origin: {}, same_origin: {}}; 1.92 + this._iframe = iframe; 1.93 + this._countedTests = 0; 1.94 +} 1.95 +ThingyListener.prototype = { 1.96 + 1.97 + observe: function(subject, topic, data) { 1.98 + // make sure to only observe app-generated calls to the helper for this test. 1.99 + var testpat = new RegExp("file_csp_bug773891\\.sjs"); 1.100 + 1.101 + // used to extract which kind of load this is (img, script, etc). 1.102 + var typepat = new RegExp("type=([\\_a-z0-9]+)"); 1.103 + 1.104 + // used to identify whether it's cross-origin or same-origin loads 1.105 + // (the applied CSP allows same-origin loads). 1.106 + var originpat = new RegExp("origin=([\\_a-z0-9]+)"); 1.107 + 1.108 + if (topic === "http-on-modify-request") { 1.109 + // Matching requests on this topic were allowed by the csp 1.110 + var chan = subject.QueryInterface(Components.interfaces.nsIHttpChannel); 1.111 + var uri = chan.URI; 1.112 + // ignore irrelevent URIs 1.113 + if (!testpat.test(uri.asciiSpec)) return; 1.114 + 1.115 + var loadType = typepat.exec(uri.asciiSpec)[1]; 1.116 + var originType = originpat.exec(uri.asciiSpec)[1]; 1.117 + 1.118 + // skip duplicate hits to this topic (potentially document loads 1.119 + // may generate duplicate loads. 1.120 + if (this._resultsRecorded[originType] && 1.121 + this._resultsRecorded[originType][loadType]) { 1.122 + return; 1.123 + } 1.124 + var message = originType + " : " + loadType + " should be " + 1.125 + (this._expectedResults[originType][loadType] ? "allowed" : "blocked"); 1.126 + ok(this._expectedResults[originType][loadType] == true, message); 1.127 + this._resultsRecorded[originType][loadType] = true; 1.128 + this._countedTests++; 1.129 + } 1.130 + else if (topic === "csp-on-violate-policy") { 1.131 + // Matching hits on this topic were blocked by the csp 1.132 + var uri = subject.QueryInterface(Components.interfaces.nsIURI); 1.133 + // ignore irrelevent URIs 1.134 + if (!testpat.test(uri.asciiSpec)) return; 1.135 + 1.136 + var loadType = typepat.exec(uri.asciiSpec)[1]; 1.137 + var originType = originpat.exec(uri.asciiSpec)[1]; 1.138 + 1.139 + // skip duplicate hits to this topic (potentially document loads 1.140 + // may generate duplicate loads. 1.141 + if (this._resultsRecorded[originType] && 1.142 + this._resultsRecorded[originType][loadType]) { 1.143 + return; 1.144 + } 1.145 + 1.146 + var message = originType + " : " + loadType + " should be " + 1.147 + (this._expectedResults[originType][loadType] ? "allowed" : "blocked"); 1.148 + ok(this._expectedResults[originType][loadType] == false, message); 1.149 + this._resultsRecorded[originType][loadType] = true; 1.150 + this._countedTests++; 1.151 + } 1.152 + else { 1.153 + // wrong topic! Nothing to do. 1.154 + return; 1.155 + } 1.156 + 1.157 + this._checkForFinish(); 1.158 + }, 1.159 + 1.160 + _checkForFinish: function() { 1.161 + // check to see if there are load tests still pending. 1.162 + // (All requests triggered by the app should hit one of the 1.163 + // two observer topics.) 1.164 + if (this._countedTests == this._expectedResults.max_tests) { 1.165 + Services.obs.removeObserver(this, "csp-on-violate-policy"); 1.166 + Services.obs.removeObserver(this, "http-on-modify-request"); 1.167 + dump("removed observers\n"); 1.168 + checkedCount++; 1.169 + if (checkedCount == checksTodo) { 1.170 + SpecialPowers.removePermission("browser", "https://example.com"); 1.171 + SimpleTest.finish(); 1.172 + } else { 1.173 + gTestRunner.next(); 1.174 + } 1.175 + } 1.176 + }, 1.177 + 1.178 + // verify the status of the app 1.179 + checkAppStatus: function() { 1.180 + var principal = this._iframe.contentDocument.nodePrincipal; 1.181 + if (this._testData.app) { 1.182 + is(principal.appStatus, this._testData.appStatus, 1.183 + "iframe principal's app status doesn't match the expected app status."); 1.184 + this._countedTests++; 1.185 + this._checkForFinish(); 1.186 + } 1.187 + } 1.188 +} 1.189 + 1.190 +var content = document.getElementById('content'); 1.191 +var checkedCount = 0; // number of apps checked 1.192 +var checksTodo = gData.length; 1.193 + 1.194 +// quick check to make sure we can test apps: 1.195 +is('appStatus' in document.nodePrincipal, true, 1.196 + 'appStatus should be present in nsIPrincipal, if not the rest of this test will fail'); 1.197 + 1.198 +function runTest() { 1.199 + for (var i = 0; i < gData.length; i++) { 1.200 + let data = gData[i]; 1.201 + var iframe = document.createElement('iframe'); 1.202 + 1.203 + // watch for successes and failures 1.204 + var examiner = new ThingyListener(data, iframe); 1.205 + 1.206 + iframe.setAttribute('mozapp', data.app); 1.207 + iframe.setAttribute('mozbrowser', 'true'); 1.208 + iframe.addEventListener('load', examiner.checkAppStatus.bind(examiner)); 1.209 + iframe.src = data.uri; 1.210 + 1.211 + content.appendChild(iframe); 1.212 + 1.213 + yield undefined; 1.214 + } 1.215 +} 1.216 + 1.217 +var gTestRunner = runTest(); 1.218 + 1.219 +// load the default CSP and pref it on 1.220 +SpecialPowers.addPermission("browser", true, "https://example.com"); 1.221 + 1.222 +SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true], 1.223 + ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV], 1.224 + ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]}, 1.225 + function() { gTestRunner.next(); }); 1.226 + 1.227 + 1.228 +</script> 1.229 +</pre> 1.230 +</body> 1.231 +</html>