tor-browser

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

sriharness.js (7936B)


      1 // `integrityValue` indicates the 'integrity' attribute value at the time of
      2 // #prepare-a-script.
      3 //
      4 // `integrityValueAfterPrepare` indicates how the 'integrity' attribute value
      5 // is modified after #prepare-a-script:
      6 // - `undefined` => not modified.
      7 // - `null` => 'integrity' attribute is removed.
      8 // - others => 'integrity' attribute value is set to that value.
      9 //
     10 // TODO: Make the arguments a dictionary for readability in the test files.
     11 var SRIScriptTest = function(pass, name, src, integrityValue, crossoriginValue, nonce, integrityValueAfterPrepare) {
     12    this.pass = pass;
     13    this.name = "Script: " + name;
     14    this.src = src;
     15    this.integrityValue = integrityValue;
     16    this.crossoriginValue = crossoriginValue;
     17    this.nonce = nonce;
     18    this.integrityValueAfterPrepare = integrityValueAfterPrepare;
     19 }
     20 
     21 SRIScriptTest.prototype.execute = function() {
     22    var test = async_test(this.name);
     23    var e = document.createElement("script");
     24    e.src = this.src;
     25    if (this.integrityValue) {
     26      e.setAttribute("integrity", this.integrityValue);
     27    }
     28    if(this.crossoriginValue) {
     29        e.setAttribute("crossorigin", this.crossoriginValue);
     30    }
     31    if(this.nonce) {
     32      e.setAttribute("nonce", this.nonce);
     33    }
     34    if(this.pass) {
     35        e.addEventListener("load", function() {test.done()});
     36        e.addEventListener("error", function() {
     37            test.step(function(){ assert_unreached("Good load fired error handler.") })
     38        });
     39    } else {
     40       e.addEventListener("load", function() {
     41            test.step(function() { assert_unreached("Bad load succeeded.") })
     42        });
     43       e.addEventListener("error", function() {test.done()});
     44    }
     45    document.body.appendChild(e);
     46 
     47    if (this.integrityValueAfterPrepare === null) {
     48      e.removeAttribute("integrity");
     49    } else if (this.integrityValueAfterPrepare !== undefined) {
     50      e.setAttribute("integrity", this.integrityValueAfterPrepare);
     51    }
     52 };
     53 
     54 function set_extra_attributes(element, attrs) {
     55  // Apply the rest of the attributes, if any.
     56  for (const [attr_name, attr_val] of Object.entries(attrs)) {
     57    element[attr_name] = attr_val;
     58  }
     59 }
     60 
     61 function buildElementFromDestination(resource_url, destination, attrs) {
     62  // Assert: |destination| is a valid destination.
     63  let element;
     64 
     65  // The below switch is responsible for:
     66  //   1. Creating the correct subresource element
     67  //   2. Setting said element's href, src, or fetch-instigating property
     68  //      appropriately.
     69  switch (destination) {
     70    case "script":
     71      element = document.createElement(destination);
     72      set_extra_attributes(element, attrs);
     73      element.src = resource_url;
     74      break;
     75    case "style":
     76      element = document.createElement('link');
     77      set_extra_attributes(element, attrs);
     78      element.rel = 'stylesheet';
     79      element.href = resource_url;
     80      break;
     81    case "image":
     82      element = document.createElement('img');
     83      set_extra_attributes(element, attrs);
     84      element.src = resource_url;
     85      break;
     86    default:
     87      assert_unreached("INVALID DESTINATION");
     88  }
     89 
     90  return element;
     91 }
     92 
     93 // When using SRIPreloadTest, also include /preload/resources/preload_helper.js
     94 // |number_of_requests| is used to ensure that preload requests are actually
     95 // reused as expected.
     96 const SRIPreloadTest = (preload_sri_success, subresource_sri_success, name,
     97                        number_of_requests, destination, resource_url,
     98                        link_attrs, subresource_attrs) => {
     99  const test = async_test(name);
    100  const link = document.createElement('link');
    101 
    102  // Early-fail in UAs that do not support `preload` links.
    103  test.step_func(() => {
    104    assert_true(link.relList.supports('preload'),
    105      "This test is automatically failing because the browser does not" +
    106      "support `preload` links.");
    107  })();
    108 
    109  // Build up the link.
    110  link.rel = 'preload';
    111  link.as = destination;
    112  link.href = resource_url;
    113  for (const [attr_name, attr_val] of Object.entries(link_attrs)) {
    114    link[attr_name] = attr_val; // This may override `rel` to modulepreload.
    115  }
    116 
    117  // Preload + subresource success and failure loading functions.
    118  const valid_preload_failed = test.step_func(() =>
    119    { assert_unreached("Valid preload fired error handler.") });
    120  const invalid_preload_succeeded = test.step_func(() =>
    121    { assert_unreached("Invalid preload load succeeded.") });
    122  const valid_subresource_failed = test.step_func(() =>
    123    { assert_unreached("Valid subresource fired error handler.") });
    124  const invalid_subresource_succeeded = test.step_func(() =>
    125    { assert_unreached("Invalid subresource load succeeded.") });
    126  const subresource_pass = test.step_func(() => {
    127    verifyNumberOfResourceTimingEntries(resource_url, number_of_requests);
    128    test.done();
    129  });
    130  const preload_pass = test.step_func(() => {
    131    const subresource_element = buildElementFromDestination(
    132      resource_url,
    133      destination,
    134      subresource_attrs
    135    );
    136 
    137    if (subresource_sri_success) {
    138      subresource_element.onload = subresource_pass;
    139      subresource_element.onerror = valid_subresource_failed;
    140    } else {
    141      subresource_element.onload = invalid_subresource_succeeded;
    142      subresource_element.onerror = subresource_pass;
    143    }
    144 
    145    document.body.append(subresource_element);
    146  });
    147 
    148  if (preload_sri_success) {
    149    link.onload = preload_pass;
    150    link.onerror = valid_preload_failed;
    151  } else {
    152    link.onload = invalid_preload_succeeded;
    153    link.onerror = preload_pass;
    154  }
    155 
    156  document.head.append(link);
    157 }
    158 
    159 // <link> tests
    160 // Style tests must be done synchronously because they rely on the presence
    161 // and absence of global style, which can affect later tests. Thus, instead
    162 // of executing them one at a time, the style tests are implemented as a
    163 // queue that builds up a list of tests, and then executes them one at a
    164 // time.
    165 var SRIStyleTest = function(queue, pass, name, attrs, customCallback, altPassValue) {
    166    this.pass = pass;
    167    this.name = "Style: " + name;
    168    this.customCallback = customCallback || function () {};
    169    this.attrs = attrs || {};
    170    this.passValue = altPassValue || "rgb(255, 255, 0)";
    171 
    172    this.test = async_test(this.name);
    173 
    174    this.queue = queue;
    175    this.queue.push(this);
    176 }
    177 
    178 SRIStyleTest.prototype.execute = function() {
    179  var that = this;
    180    var container = document.getElementById("container");
    181    while (container.hasChildNodes()) {
    182      container.removeChild(container.firstChild);
    183    }
    184 
    185    var test = this.test;
    186 
    187    var div = document.createElement("div");
    188    div.className = "testdiv";
    189    var e = document.createElement("link");
    190 
    191    // The link relation is guaranteed to not be "preload" or "modulepreload".
    192    this.attrs.rel = this.attrs.rel || "stylesheet";
    193    for (var key in this.attrs) {
    194        if (this.attrs.hasOwnProperty(key)) {
    195            e.setAttribute(key, this.attrs[key]);
    196        }
    197    }
    198 
    199    if(this.pass) {
    200        e.addEventListener("load", function() {
    201            test.step(function() {
    202                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
    203                assert_equals(background, that.passValue);
    204                test.done();
    205            });
    206        });
    207        e.addEventListener("error", function() {
    208            test.step(function(){ assert_unreached("Good load fired error handler.") })
    209        });
    210    } else {
    211        e.addEventListener("load", function() {
    212             test.step(function() { assert_unreached("Bad load succeeded.") })
    213         });
    214        e.addEventListener("error", function() {
    215            test.step(function() {
    216                var background = window.getComputedStyle(div, null).getPropertyValue("background-color");
    217                assert_not_equals(background, that.passValue);
    218                test.done();
    219            });
    220        });
    221    }
    222    container.appendChild(div);
    223    container.appendChild(e);
    224    this.customCallback(e, container);
    225 };