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