tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_csp_reports.js (8778B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const { NetUtil } = ChromeUtils.importESModule(
      6  "resource://gre/modules/NetUtil.sys.mjs"
      7 );
      8 const { HttpServer } = ChromeUtils.importESModule(
      9  "resource://testing-common/httpd.sys.mjs"
     10 );
     11 
     12 var httpServer = new HttpServer();
     13 httpServer.start(-1);
     14 var testsToFinish = 0;
     15 
     16 var principal;
     17 
     18 const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
     19 const REPORT_SERVER_URI = "http://localhost";
     20 
     21 /**
     22 * Construct a callback that listens to a report submission and either passes
     23 * or fails a test based on what it gets.
     24 */
     25 function makeReportHandler(testpath, message, expectedJSON) {
     26  return function (request) {
     27    // we only like "POST" submissions for reports!
     28    if (request.method !== "POST") {
     29      do_throw("violation report should be a POST request");
     30      return;
     31    }
     32 
     33    // check content-type of report is "application/csp-report"
     34    var contentType = request.hasHeader("Content-Type")
     35      ? request.getHeader("Content-Type")
     36      : undefined;
     37    if (contentType !== "application/csp-report") {
     38      do_throw(
     39        "violation report should have the 'application/csp-report' " +
     40          "content-type, when in fact it is " +
     41          contentType.toString()
     42      );
     43    }
     44 
     45    // obtain violation report
     46    var reportObj = JSON.parse(
     47      NetUtil.readInputStreamToString(
     48        request.bodyInputStream,
     49        request.bodyInputStream.available()
     50      )
     51    );
     52 
     53    // dump("GOT REPORT:\n" + JSON.stringify(reportObj) + "\n");
     54    // dump("TESTPATH:    " + testpath + "\n");
     55    // dump("EXPECTED:  \n" + JSON.stringify(expectedJSON) + "\n\n");
     56 
     57    for (var i in expectedJSON) {
     58      Assert.equal(expectedJSON[i], reportObj["csp-report"][i]);
     59    }
     60 
     61    testsToFinish--;
     62    httpServer.registerPathHandler(testpath, null);
     63    if (testsToFinish < 1) {
     64      httpServer.stop(do_test_finished);
     65    } else {
     66      do_test_finished();
     67    }
     68  };
     69 }
     70 
     71 /**
     72 * Everything created by this assumes it will cause a report.  If you want to
     73 * add a test here that will *not* cause a report to go out, you're gonna have
     74 * to make sure the test cleans up after itself.
     75 */
     76 function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
     77  testsToFinish++;
     78  do_test_pending();
     79 
     80  // set up a new CSP instance for each test.
     81  var csp = Cc["@mozilla.org/cspcontext;1"].createInstance(
     82    Ci.nsIContentSecurityPolicy
     83  );
     84  var policy =
     85    "default-src 'none' 'report-sample'; " +
     86    "report-uri " +
     87    REPORT_SERVER_URI +
     88    ":" +
     89    REPORT_SERVER_PORT +
     90    "/test" +
     91    id;
     92  var selfuri = NetUtil.newURI(
     93    REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self"
     94  );
     95 
     96  dump("Created test " + id + " : " + policy + "\n\n");
     97 
     98  principal = Services.scriptSecurityManager.createContentPrincipal(
     99    selfuri,
    100    {}
    101  );
    102  csp.setRequestContextWithPrincipal(principal, selfuri, "", 0);
    103 
    104  // Load up the policy
    105  // set as report-only if that's the case
    106  csp.appendPolicy(policy, useReportOnlyPolicy, false);
    107 
    108  // prime the report server
    109  var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
    110  httpServer.registerPathHandler("/test" + id, handler);
    111 
    112  // trigger the violation
    113  callback(csp);
    114 }
    115 
    116 function run_test() {
    117  do_get_profile();
    118 
    119  var selfuri = NetUtil.newURI(
    120    REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self"
    121  );
    122 
    123  // test that inline script violations cause a report.
    124  makeTest(
    125    0,
    126    {
    127      "blocked-uri": "inline",
    128      "effective-directive": "script-src-elem",
    129      disposition: "enforce",
    130    },
    131    false,
    132    function (csp) {
    133      let inlineOK = true;
    134      inlineOK = csp.getAllowsInline(
    135        Ci.nsIContentSecurityPolicy.SCRIPT_SRC_ELEM_DIRECTIVE,
    136        false, // aHasUnsafeHash
    137        "", // aNonce
    138        false, // aParserCreated
    139        null, // aTriggeringElement
    140        null, // nsICSPEventListener
    141        "", // aContentOfPseudoScript
    142        0, // aLineNumber
    143        1 // aColumnNumber
    144      );
    145 
    146      // this is not a report only policy, so it better block inline scripts
    147      Assert.ok(!inlineOK);
    148    }
    149  );
    150 
    151  // test that eval violations cause a report.
    152  makeTest(
    153    1,
    154    {
    155      "blocked-uri": "eval",
    156      // JSON script-sample is UTF8 encoded
    157      "script-sample": "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9",
    158      "line-number": 1,
    159      "column-number": 2,
    160    },
    161    false,
    162    function (csp) {
    163      let evalOK = true,
    164        oReportViolation = { value: false };
    165      evalOK = csp.getAllowsEval(oReportViolation);
    166 
    167      // this is not a report only policy, so it better block eval
    168      Assert.ok(!evalOK);
    169      // ... and cause reports to go out
    170      Assert.ok(oReportViolation.value);
    171 
    172      if (oReportViolation.value) {
    173        // force the logging, since the getter doesn't.
    174        csp.logViolationDetails(
    175          Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
    176          null, // aTriggeringElement
    177          null, // nsICSPEventListener
    178          selfuri.asciiSpec,
    179          // sending UTF-16 script sample to make sure
    180          // csp report in JSON is not cut-off, please
    181          // note that JSON is UTF8 encoded.
    182          "\u00a3\u00a5\u00b5\u5317\ud841\udf79",
    183          1, // line number
    184          2 // column number
    185        );
    186      }
    187    }
    188  );
    189 
    190  makeTest(
    191    2,
    192    { "blocked-uri": "http://blocked.test/foo.js" },
    193    false,
    194    function (csp) {
    195      // shouldLoad creates and sends out the report here.
    196      csp.shouldLoad(
    197        Ci.nsIContentPolicy.TYPE_SCRIPT,
    198        null, // nsICSPEventListener
    199        null, // aLoadInfo
    200        NetUtil.newURI("http://blocked.test/foo.js"),
    201        null,
    202        true
    203      );
    204    }
    205  );
    206 
    207  // test that inline script violations cause a report in report-only policy
    208  makeTest(
    209    3,
    210    { "blocked-uri": "inline", disposition: "report" },
    211    true,
    212    function (csp) {
    213      let inlineOK = true;
    214      inlineOK = csp.getAllowsInline(
    215        Ci.nsIContentSecurityPolicy.SCRIPT_SRC_ELEM_DIRECTIVE,
    216        false, // aHasUnsafeHash
    217        "", // aNonce
    218        false, // aParserCreated
    219        null, // aTriggeringElement
    220        null, // nsICSPEventListener
    221        "", // aContentOfPseudoScript
    222        0, // aLineNumber
    223        1 // aColumnNumber
    224      );
    225 
    226      // this is a report only policy, so it better allow inline scripts
    227      Assert.ok(inlineOK);
    228    }
    229  );
    230 
    231  // test that eval violations cause a report in report-only policy
    232  makeTest(4, { "blocked-uri": "eval" }, true, function (csp) {
    233    let evalOK = true,
    234      oReportViolation = { value: false };
    235    evalOK = csp.getAllowsEval(oReportViolation);
    236 
    237    // this is a report only policy, so it better allow eval
    238    Assert.ok(evalOK);
    239    // ... but still cause reports to go out
    240    Assert.ok(oReportViolation.value);
    241 
    242    if (oReportViolation.value) {
    243      // force the logging, since the getter doesn't.
    244      csp.logViolationDetails(
    245        Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
    246        null, // aTriggeringElement
    247        null, // nsICSPEventListener
    248        selfuri.asciiSpec,
    249        "script sample",
    250        4, // line number
    251        5 // column number
    252      );
    253    }
    254  });
    255 
    256  // test that only the uri's scheme is reported for globally unique identifiers
    257  makeTest(5, { "blocked-uri": "data" }, false, function (csp) {
    258    var base64data =
    259      "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
    260      "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
    261    // shouldLoad creates and sends out the report here.
    262    csp.shouldLoad(
    263      Ci.nsIContentPolicy.TYPE_IMAGE,
    264      null, // nsICSPEventListener
    265      null, // nsILoadInfo
    266      NetUtil.newURI("data:image/png;base64," + base64data),
    267      null,
    268      true
    269    );
    270  });
    271 
    272  // test that only the uri's scheme is reported for globally unique identifiers
    273  makeTest(6, { "blocked-uri": "intent" }, false, function (csp) {
    274    // shouldLoad creates and sends out the report here.
    275    csp.shouldLoad(
    276      Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
    277      null, // nsICSPEventListener
    278      null, // nsILoadInfo
    279      NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"),
    280      null,
    281      true
    282    );
    283  });
    284 
    285  // test fragment removal
    286  var selfSpec =
    287    REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js";
    288  makeTest(7, { "blocked-uri": selfSpec }, false, function (csp) {
    289    // shouldLoad creates and sends out the report here.
    290    csp.shouldLoad(
    291      Ci.nsIContentPolicy.TYPE_SCRIPT,
    292      null, // nsICSPEventListener
    293      null, // nsILoadInfo
    294      NetUtil.newURI(selfSpec + "#bar"),
    295      null,
    296      true
    297    );
    298  });
    299 }