content/base/test/csp/test_csp_bug773891.html

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 <!DOCTYPE HTML>
michael@0 2 <html>
michael@0 3 <!--
michael@0 4 https://bugzilla.mozilla.org/show_bug.cgi?id=768029
michael@0 5 -->
michael@0 6 <head>
michael@0 7 <meta charset="utf-8">
michael@0 8 <title>Test for CSP on trusted/certified and installed apps -- bug 773891</title>
michael@0 9 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
michael@0 10 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
michael@0 11 </head>
michael@0 12 <body>
michael@0 13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=773891">Mozilla Bug 773891</a>
michael@0 14 <p id="display"></p>
michael@0 15 <div id="content">
michael@0 16
michael@0 17 </div>
michael@0 18 <pre id="test">
michael@0 19 <script type="application/javascript;version=1.7">
michael@0 20
michael@0 21 Components.utils.import("resource://gre/modules/Services.jsm");
michael@0 22
michael@0 23 /** Test for Bug 773891 **/
michael@0 24
michael@0 25 // Note: we don't have to inspect all the different operations of CSP,
michael@0 26 // we're just looking for specific differences in behavior that indicate
michael@0 27 // a default CSP got applied.
michael@0 28 const DEFAULT_CSP_PRIV = "default-src *; script-src *; style-src 'self' 'unsafe-inline'; object-src 'none'";
michael@0 29 const DEFAULT_CSP_CERT = "default-src *; script-src *; style-src 'self'; object-src 'none'";
michael@0 30
michael@0 31 const MANIFEST_CSP_PRIV = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'";
michael@0 32 const MANIFEST_CSP_INST = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'";
michael@0 33 const MANIFEST_CSP_CERT = "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'";
michael@0 34
michael@0 35 SimpleTest.waitForExplicitFinish();
michael@0 36
michael@0 37 var gData = [
michael@0 38
michael@0 39 {
michael@0 40 app: "https://example.com/manifest_csp_inst.webapp",
michael@0 41 appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
michael@0 42 csp: MANIFEST_CSP_INST,
michael@0 43 origin: "https://example.com",
michael@0 44 uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html",
michael@0 45 statusString: "installed app",
michael@0 46 expectedTestResults: {
michael@0 47 max_tests: 7, /* number of bools below plus one for the status check */
michael@0 48 cross_origin: { img: true, script: false, style: false },
michael@0 49 same_origin: { img: true, script: true, style: true },
michael@0 50 },
michael@0 51 },
michael@0 52 {
michael@0 53 app: "https://example.com/manifest_csp_cert.webapp",
michael@0 54 appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED,
michael@0 55 csp: MANIFEST_CSP_CERT,
michael@0 56 origin: "https://example.com",
michael@0 57 uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html",
michael@0 58 statusString: "certified app",
michael@0 59 expectedTestResults: {
michael@0 60 max_tests: 7, /* number of bools below plus one for the status check */
michael@0 61 cross_origin: { img: true, script: false, style: false },
michael@0 62 same_origin: { img: true, script: true, style: true },
michael@0 63 },
michael@0 64 },
michael@0 65 {
michael@0 66 app: "https://example.com/manifest_csp_priv.webapp",
michael@0 67 appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
michael@0 68 csp: MANIFEST_CSP_PRIV,
michael@0 69 origin: "https://example.com",
michael@0 70 uri: "https://example.com/tests/content/base/test/csp/file_csp_bug773891.html",
michael@0 71 statusString: "privileged app",
michael@0 72 expectedTestResults: {
michael@0 73 max_tests: 7, /* number of bools below plus one for the status check */
michael@0 74 cross_origin: { img: true, script: false, style: false },
michael@0 75 same_origin: { img: true, script: true, style: true },
michael@0 76 },
michael@0 77 },
michael@0 78 ];
michael@0 79
michael@0 80 // Observer for watching allowed loads and blocked attempts
michael@0 81 function ThingyListener(app, iframe) {
michael@0 82 Services.obs.addObserver(this, "csp-on-violate-policy", false);
michael@0 83 Services.obs.addObserver(this, "http-on-modify-request", false);
michael@0 84 dump("added observers\n");
michael@0 85 // keep track of which app ID this test is monitoring.
michael@0 86 this._testData = app;
michael@0 87 this._expectedResults = app.expectedTestResults;
michael@0 88 this._resultsRecorded = { cross_origin: {}, same_origin: {}};
michael@0 89 this._iframe = iframe;
michael@0 90 this._countedTests = 0;
michael@0 91 }
michael@0 92 ThingyListener.prototype = {
michael@0 93
michael@0 94 observe: function(subject, topic, data) {
michael@0 95 // make sure to only observe app-generated calls to the helper for this test.
michael@0 96 var testpat = new RegExp("file_csp_bug773891\\.sjs");
michael@0 97
michael@0 98 // used to extract which kind of load this is (img, script, etc).
michael@0 99 var typepat = new RegExp("type=([\\_a-z0-9]+)");
michael@0 100
michael@0 101 // used to identify whether it's cross-origin or same-origin loads
michael@0 102 // (the applied CSP allows same-origin loads).
michael@0 103 var originpat = new RegExp("origin=([\\_a-z0-9]+)");
michael@0 104
michael@0 105 if (topic === "http-on-modify-request") {
michael@0 106 // Matching requests on this topic were allowed by the csp
michael@0 107 var chan = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
michael@0 108 var uri = chan.URI;
michael@0 109 // ignore irrelevent URIs
michael@0 110 if (!testpat.test(uri.asciiSpec)) return;
michael@0 111
michael@0 112 var loadType = typepat.exec(uri.asciiSpec)[1];
michael@0 113 var originType = originpat.exec(uri.asciiSpec)[1];
michael@0 114
michael@0 115 // skip duplicate hits to this topic (potentially document loads
michael@0 116 // may generate duplicate loads.
michael@0 117 if (this._resultsRecorded[originType] &&
michael@0 118 this._resultsRecorded[originType][loadType]) {
michael@0 119 return;
michael@0 120 }
michael@0 121 var message = originType + " : " + loadType + " should be " +
michael@0 122 (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
michael@0 123 ok(this._expectedResults[originType][loadType] == true, message);
michael@0 124 this._resultsRecorded[originType][loadType] = true;
michael@0 125 this._countedTests++;
michael@0 126 }
michael@0 127 else if (topic === "csp-on-violate-policy") {
michael@0 128 // Matching hits on this topic were blocked by the csp
michael@0 129 var uri = subject.QueryInterface(Components.interfaces.nsIURI);
michael@0 130 // ignore irrelevent URIs
michael@0 131 if (!testpat.test(uri.asciiSpec)) return;
michael@0 132
michael@0 133 var loadType = typepat.exec(uri.asciiSpec)[1];
michael@0 134 var originType = originpat.exec(uri.asciiSpec)[1];
michael@0 135
michael@0 136 // skip duplicate hits to this topic (potentially document loads
michael@0 137 // may generate duplicate loads.
michael@0 138 if (this._resultsRecorded[originType] &&
michael@0 139 this._resultsRecorded[originType][loadType]) {
michael@0 140 return;
michael@0 141 }
michael@0 142
michael@0 143 var message = originType + " : " + loadType + " should be " +
michael@0 144 (this._expectedResults[originType][loadType] ? "allowed" : "blocked");
michael@0 145 ok(this._expectedResults[originType][loadType] == false, message);
michael@0 146 this._resultsRecorded[originType][loadType] = true;
michael@0 147 this._countedTests++;
michael@0 148 }
michael@0 149 else {
michael@0 150 // wrong topic! Nothing to do.
michael@0 151 return;
michael@0 152 }
michael@0 153
michael@0 154 this._checkForFinish();
michael@0 155 },
michael@0 156
michael@0 157 _checkForFinish: function() {
michael@0 158 // check to see if there are load tests still pending.
michael@0 159 // (All requests triggered by the app should hit one of the
michael@0 160 // two observer topics.)
michael@0 161 if (this._countedTests == this._expectedResults.max_tests) {
michael@0 162 Services.obs.removeObserver(this, "csp-on-violate-policy");
michael@0 163 Services.obs.removeObserver(this, "http-on-modify-request");
michael@0 164 dump("removed observers\n");
michael@0 165 checkedCount++;
michael@0 166 if (checkedCount == checksTodo) {
michael@0 167 SpecialPowers.removePermission("browser", "https://example.com");
michael@0 168 SimpleTest.finish();
michael@0 169 } else {
michael@0 170 gTestRunner.next();
michael@0 171 }
michael@0 172 }
michael@0 173 },
michael@0 174
michael@0 175 // verify the status of the app
michael@0 176 checkAppStatus: function() {
michael@0 177 var principal = this._iframe.contentDocument.nodePrincipal;
michael@0 178 if (this._testData.app) {
michael@0 179 is(principal.appStatus, this._testData.appStatus,
michael@0 180 "iframe principal's app status doesn't match the expected app status.");
michael@0 181 this._countedTests++;
michael@0 182 this._checkForFinish();
michael@0 183 }
michael@0 184 }
michael@0 185 }
michael@0 186
michael@0 187 var content = document.getElementById('content');
michael@0 188 var checkedCount = 0; // number of apps checked
michael@0 189 var checksTodo = gData.length;
michael@0 190
michael@0 191 // quick check to make sure we can test apps:
michael@0 192 is('appStatus' in document.nodePrincipal, true,
michael@0 193 'appStatus should be present in nsIPrincipal, if not the rest of this test will fail');
michael@0 194
michael@0 195 function runTest() {
michael@0 196 for (var i = 0; i < gData.length; i++) {
michael@0 197 let data = gData[i];
michael@0 198 var iframe = document.createElement('iframe');
michael@0 199
michael@0 200 // watch for successes and failures
michael@0 201 var examiner = new ThingyListener(data, iframe);
michael@0 202
michael@0 203 iframe.setAttribute('mozapp', data.app);
michael@0 204 iframe.setAttribute('mozbrowser', 'true');
michael@0 205 iframe.addEventListener('load', examiner.checkAppStatus.bind(examiner));
michael@0 206 iframe.src = data.uri;
michael@0 207
michael@0 208 content.appendChild(iframe);
michael@0 209
michael@0 210 yield undefined;
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 var gTestRunner = runTest();
michael@0 215
michael@0 216 // load the default CSP and pref it on
michael@0 217 SpecialPowers.addPermission("browser", true, "https://example.com");
michael@0 218
michael@0 219 SpecialPowers.pushPrefEnv({'set': [["dom.mozBrowserFramesEnabled", true],
michael@0 220 ["security.apps.privileged.CSP.default", DEFAULT_CSP_PRIV],
michael@0 221 ["security.apps.certified.CSP.default", DEFAULT_CSP_CERT]]},
michael@0 222 function() { gTestRunner.next(); });
michael@0 223
michael@0 224
michael@0 225 </script>
michael@0 226 </pre>
michael@0 227 </body>
michael@0 228 </html>

mercurial