content/base/test/csp/test_csp_bug768029.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

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

mercurial