tor-browser

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

storagePermissionsUtils.js (9736B)


      1 const BEHAVIOR_ACCEPT = 0;
      2 const BEHAVIOR_REJECT_FOREIGN = 1;
      3 const BEHAVIOR_REJECT = 2;
      4 const BEHAVIOR_LIMIT_FOREIGN = 3;
      5 
      6 const kPrefName = "network.cookie.cookieBehavior";
      7 
      8 // Check if we are in frame, and declare ok and finishTest appropriately
      9 const inFrame = ("" + location).match(/frame/);
     10 if (inFrame) {
     11  ok = function (a, message) {
     12    if (!a) {
     13      parent.postMessage("FAILURE: " + message, "http://mochi.test:8888");
     14    } else {
     15      parent.postMessage(message, "http://mochi.test:8888");
     16    }
     17  };
     18 
     19  finishTest = function () {
     20    parent.postMessage("done", "http://mochi.test:8888");
     21  };
     22 } else {
     23  finishTest = function () {
     24    SimpleTest.finish();
     25  };
     26 }
     27 
     28 function setCookieBehavior(behavior) {
     29  return SpecialPowers.pushPrefEnv({ set: [[kPrefName, behavior]] });
     30 }
     31 
     32 function runIFrame(url) {
     33  return new Promise((resolve, reject) => {
     34    function onMessage(e) {
     35      if (e.data == "done") {
     36        resolve();
     37        window.removeEventListener("message", onMessage);
     38        return;
     39      }
     40 
     41      const isFail = e.data.match(/^FAILURE/);
     42      ok(!isFail, e.data + " (IFRAME = " + url + ")");
     43      if (isFail) {
     44        reject(e);
     45      }
     46    }
     47    window.addEventListener("message", onMessage);
     48 
     49    document.querySelector("iframe").src = url;
     50  });
     51 }
     52 
     53 function runWorker(url) {
     54  return new Promise((resolve, reject) => {
     55    var worker = new Worker(url);
     56    worker.addEventListener("message", function (e) {
     57      if (e.data == "done") {
     58        resolve();
     59        return;
     60      }
     61 
     62      ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = " + url + ")");
     63    });
     64 
     65    worker.addEventListener("error", function (e) {
     66      ok(false, e.data + " (WORKER = " + url + ")");
     67      reject(e);
     68    });
     69  });
     70 }
     71 
     72 function chromePower(allowed, blockSessionStorage) {
     73  // localStorage is affected by storage policy.
     74  try {
     75    SpecialPowers.wrap(window).localStorage.getItem("X");
     76    ok(allowed, "getting localStorage from chrome didn't throw");
     77  } catch (e) {
     78    ok(!allowed, "getting localStorage from chrome threw");
     79  }
     80 
     81  // sessionStorage is not. See bug 1183968.
     82  try {
     83    SpecialPowers.wrap(window).sessionStorage.getItem("X");
     84    ok(!blockSessionStorage, "getting sessionStorage from chrome didn't throw");
     85  } catch (e) {
     86    ok(blockSessionStorage, "getting sessionStorage from chrome threw");
     87  }
     88 
     89  // indexedDB is affected by storage policy.
     90  try {
     91    SpecialPowers.wrap(window).indexedDB;
     92    ok(allowed, "getting indexedDB from chrome didn't throw");
     93  } catch (e) {
     94    ok(!allowed, "getting indexedDB from chrome threw");
     95  }
     96 
     97  // Same with caches, along with the additional https-only requirement.
     98  try {
     99    var shouldResolve = allowed && location.protocol == "https:";
    100    var promise = SpecialPowers.wrap(window).caches.keys();
    101    ok(true, "getting caches from chrome should never throw");
    102    return new Promise((resolve, reject) => {
    103      promise.then(
    104        function () {
    105          ok(shouldResolve, "The promise was resolved for chrome");
    106          resolve();
    107        },
    108        function (e) {
    109          ok(!shouldResolve, "The promise was rejected for chrome: " + e);
    110          resolve();
    111        }
    112      );
    113    });
    114  } catch (e) {
    115    ok(false, "getting caches from chrome threw");
    116    return undefined;
    117  }
    118 }
    119 
    120 function storageAllowed() {
    121  try {
    122    localStorage.getItem("X");
    123    ok(true, "getting localStorage didn't throw");
    124  } catch (e) {
    125    ok(false, "getting localStorage should not throw");
    126  }
    127 
    128  try {
    129    sessionStorage.getItem("X");
    130    ok(true, "getting sessionStorage didn't throw");
    131  } catch (e) {
    132    ok(false, "getting sessionStorage should not throw");
    133  }
    134 
    135  try {
    136    indexedDB;
    137    ok(true, "getting indexedDB didn't throw");
    138  } catch (e) {
    139    ok(false, "getting indexedDB should not throw");
    140  }
    141 
    142  const dbName = "db";
    143 
    144  try {
    145    var promise = caches.keys();
    146    ok(true, "getting caches didn't throw");
    147 
    148    return new Promise((resolve, reject) => {
    149      const checkCacheKeys = () => {
    150        promise.then(
    151          () => {
    152            ok(location.protocol == "https:", "The promise was not rejected");
    153            resolve();
    154          },
    155          () => {
    156            ok(
    157              location.protocol !== "https:",
    158              "The promise should not have been rejected"
    159            );
    160            resolve();
    161          }
    162        );
    163      };
    164 
    165      const checkDeleteDbAndTheRest = dbs => {
    166        ok(
    167          dbs.some(elem => elem.name === dbName),
    168          "Expected database should be found"
    169        );
    170 
    171        const end = indexedDB.deleteDatabase(dbName);
    172        end.onsuccess = checkCacheKeys;
    173        end.onerror = err => {
    174          ok(false, "querying indexedDB databases should not throw");
    175          reject(err);
    176        };
    177      };
    178 
    179      const checkDatabasesAndTheRest = () => {
    180        indexedDB
    181          .databases()
    182          .then(checkDeleteDbAndTheRest)
    183          .catch(err => {
    184            ok(false, "deleting an indexedDB database should not throw");
    185            reject(err);
    186          });
    187      };
    188 
    189      const begin = indexedDB.open(dbName);
    190      begin.onsuccess = checkDatabasesAndTheRest;
    191      begin.onerror = err => {
    192        ok(false, "opening an indexedDB database should not throw");
    193        reject(err);
    194      };
    195    });
    196  } catch (e) {
    197    ok(location.protocol !== "https:", "getting caches should not have thrown");
    198    return Promise.resolve();
    199  }
    200 }
    201 
    202 function storagePrevented() {
    203  try {
    204    localStorage.getItem("X");
    205    ok(false, "getting localStorage should have thrown");
    206  } catch (e) {
    207    ok(true, "getting localStorage threw");
    208  }
    209 
    210  if (location.hash == "#thirdparty") {
    211    // No matter what the user's preferences are, we don't block
    212    // sessionStorage in 3rd-party iframes. We do block them everywhere
    213    // else however.
    214    try {
    215      sessionStorage.getItem("X");
    216      ok(true, "getting sessionStorage didn't throw");
    217    } catch (e) {
    218      ok(false, "getting sessionStorage should not have thrown");
    219    }
    220  } else {
    221    try {
    222      sessionStorage.getItem("X");
    223      ok(false, "getting sessionStorage should have thrown");
    224    } catch (e) {
    225      ok(true, "getting sessionStorage threw");
    226    }
    227  }
    228 
    229  try {
    230    indexedDB;
    231    ok(true, "getting indexedDB didn't throw");
    232  } catch (e) {
    233    ok(false, "getting indexedDB should not have thrown");
    234  }
    235 
    236  const dbName = "album";
    237 
    238  try {
    239    indexedDB.open(dbName);
    240    ok(false, "opening an indexedDB database didn't throw");
    241  } catch (e) {
    242    ok(true, "opening an indexedDB database threw");
    243    ok(
    244      e.name == "SecurityError",
    245      "opening indexedDB database emitted a security error"
    246    );
    247  }
    248 
    249  // Note: Security error is expected to be thrown synchronously.
    250  indexedDB.databases().then(
    251    () => {
    252      ok(false, "querying indexedDB databases didn't reject");
    253    },
    254    e => {
    255      ok(true, "querying indexedDB databases rejected");
    256      ok(
    257        e.name == "SecurityError",
    258        "querying indexedDB databases emitted a security error"
    259      );
    260    }
    261  );
    262 
    263  try {
    264    indexedDB.deleteDatabase(dbName);
    265    ok(false, "deleting an indexedDB database didn't throw");
    266  } catch (e) {
    267    ok(true, "deleting an indexedDB database threw");
    268    ok(
    269      e.name == "SecurityError",
    270      "deleting indexedDB database emitted a security error"
    271    );
    272  }
    273 
    274  try {
    275    var promise = caches.keys();
    276    ok(true, "getting caches didn't throw");
    277 
    278    return new Promise((resolve, reject) => {
    279      promise.then(
    280        function () {
    281          ok(false, "The promise should have rejected");
    282          resolve();
    283        },
    284        function () {
    285          ok(true, "The promise was rejected");
    286          resolve();
    287        }
    288      );
    289    });
    290  } catch (e) {
    291    ok(location.protocol !== "https:", "getting caches should not have thrown");
    292 
    293    return Promise.resolve();
    294  }
    295 }
    296 
    297 function task(fn) {
    298  if (!inFrame) {
    299    SimpleTest.waitForExplicitFinish();
    300  }
    301 
    302  var gen = fn();
    303 
    304  function next_step(val, e) {
    305    var it;
    306    try {
    307      if (typeof e !== "undefined") {
    308        it = gen.throw(e);
    309      } else {
    310        it = gen.next(val);
    311      }
    312    } catch (e) {
    313      ok(false, "An error was thrown while stepping: " + e);
    314      ok(false, "Stack: " + e.stack);
    315      finishTest();
    316    }
    317 
    318    if (it.done) {
    319      finishTest();
    320      return;
    321    }
    322    it.value.then(next_step, e => next_step(null, e));
    323  }
    324 
    325  if (!gen.then) {
    326    next_step();
    327  } else {
    328    gen.then(finishTest, e => {
    329      ok(false, "An error was thrown while stepping: " + e);
    330      ok(false, "Stack: " + e.stack);
    331      finishTest();
    332    });
    333  }
    334 }
    335 
    336 // The test will run on a separate window in order to apply the new cookie jar settings.
    337 async function runTestInWindow(test) {
    338  let w = window.open("window_storagePermissions.html");
    339  await new Promise(resolve => {
    340    w.onload = e => {
    341      resolve();
    342    };
    343  });
    344 
    345  return new Promise((resolve, reject) => {
    346    onmessage = e => {
    347      if (!e.data.type) {
    348        w.postMessage("FAILURE: " + e.data, document.referrer);
    349        ok(false, "No error data type");
    350        reject(e);
    351        return;
    352      }
    353 
    354      if (e.data.type == "finish") {
    355        w.close();
    356        resolve();
    357        return;
    358      }
    359 
    360      if (e.data.type == "check") {
    361        const payload = e.data.msg ? e.data.msg : e.data;
    362        ok(e.data.test, payload);
    363        const isFail = payload.match(/^FAILURE/) || !e.data.test;
    364        if (isFail) {
    365          w.postMessage("FAILURE: " + e.data, document.referrer);
    366          ok(false, payload);
    367          w.close();
    368          reject(e);
    369        }
    370        return;
    371      }
    372 
    373      ok(false, "Unknown message");
    374    };
    375 
    376    w.postMessage(test.toString(), "*");
    377  });
    378 }
    379 
    380 var thirdparty = "https://example.com/tests/dom/tests/mochitest/general/";