tor-browser

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

test_promise_job_across_sandbox.js (5706B)


      1 function createSandbox() {
      2  const uri = Services.io.newURI("https://example.com");
      3  const principal = Services.scriptSecurityManager.createContentPrincipal(
      4    uri,
      5    {}
      6  );
      7  return new Cu.Sandbox(principal, {});
      8 }
      9 
     10 add_task(async function testReactionJob() {
     11  const sandbox = createSandbox();
     12 
     13  sandbox.eval(`
     14 var testPromise = Promise.resolve(10);
     15 `);
     16 
     17  // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
     18  // on wrapped `resolve` in sandbox realm, and it fails to unwrap the security
     19  // wrapper. The reaction job should be created with sandbox realm.
     20  const p = new Promise(resolve => {
     21    sandbox.resolve = resolve;
     22 
     23    sandbox.eval(`
     24 testPromise.then(resolve);
     25 `);
     26  });
     27 
     28  const result = await p;
     29 
     30  equal(result, 10);
     31 });
     32 
     33 add_task(async function testReactionJobNuked() {
     34  const sandbox = createSandbox();
     35 
     36  sandbox.eval(`
     37 var testPromise = Promise.resolve(10);
     38 `);
     39 
     40  // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
     41  // on wrapped `resolve` in sandbox realm, and it fails to unwrap the security
     42  // wrapper. The reaction job should be created with sandbox realm.
     43  const p1 = new Promise(resolve => {
     44    sandbox.resolve = resolve;
     45 
     46    sandbox.eval(`
     47 testPromise.then(resolve);
     48 `);
     49 
     50    // Given the reaction job is created with the sandbox realm, nuking the
     51    // sandbox prevents the job gets executed.
     52    Cu.nukeSandbox(sandbox);
     53  });
     54 
     55  const p2 = Promise.resolve(11);
     56 
     57  // Given the p1 doesn't get resolved, p2 should win.
     58  const result = await Promise.race([p1, p2]);
     59 
     60  equal(result, 11);
     61 });
     62 
     63 add_task(async function testReactionJobWithXray() {
     64  const sandbox = createSandbox();
     65 
     66  sandbox.eval(`
     67 var testPromise = Promise.resolve(10);
     68 `);
     69 
     70  // Calling `Promise.prototype.then` from privileged realm via Xray uses
     71  // privileged `Promise.prototype.then` function, and GetFunctionRealm
     72  // performed there successfully gets top-level realm. The reaction job
     73  // should be created with top-level realm.
     74  const result = await new Promise(resolve => {
     75    sandbox.testPromise.then(resolve);
     76 
     77    // Given the reaction job is created with the top-level realm, nuking the
     78    // sandbox doesn't affect the reaction job.
     79    Cu.nukeSandbox(sandbox);
     80  });
     81 
     82  equal(result, 10);
     83 });
     84 
     85 add_task(async function testBoundReactionJob() {
     86  const sandbox = createSandbox();
     87 
     88  sandbox.eval(`
     89 var resolve = undefined;
     90 var callbackPromise = new Promise(r => { resolve = r; });
     91 var callback = function (v) { resolve(v + 1); };
     92 `);
     93 
     94  // Create a bound function where its realm is privileged realm, and
     95  // its target is from sandbox realm.
     96  sandbox.bound_callback = Function.prototype.bind.call(
     97    sandbox.callback,
     98    sandbox
     99  );
    100 
    101  // Calling `Promise.prototype.then` from sandbox performs GetFunctionRealm
    102  // and it fails. The reaction job should be created with sandbox realm.
    103  sandbox.eval(`
    104 Promise.resolve(10).then(bound_callback);
    105 `);
    106 
    107  const result = await sandbox.callbackPromise;
    108  equal(result, 11);
    109 });
    110 
    111 add_task(async function testThenableJob() {
    112  const sandbox = createSandbox();
    113 
    114  const p = new Promise(resolve => {
    115    // Create a bound function where its realm is privileged realm, and
    116    // its target is from sandbox realm.
    117    sandbox.then = function () {
    118      resolve(10);
    119    };
    120  });
    121 
    122  // Creating a promise thenable job in the following `Promise.resolve` performs
    123  // GetFunctionRealm on the bound thenable.then and fails. The reaction job
    124  // should be created with sandbox realm.
    125  sandbox.eval(`
    126 var thenable = {
    127  then: then,
    128 };
    129 
    130 Promise.resolve(thenable);
    131 `);
    132 
    133  const result = await p;
    134  equal(result, 10);
    135 });
    136 
    137 add_task(async function testThenableJobNuked() {
    138  const sandbox = createSandbox();
    139 
    140  let called = false;
    141  sandbox.then = function () {
    142    called = true;
    143  };
    144 
    145  // Creating a promise thenable job in the following `Promise.resolve` performs
    146  // GetFunctionRealm on the bound thenable.then and fails. The reaction job
    147  // should be created with sandbox realm.
    148  sandbox.eval(`
    149 var thenable = {
    150  then: then,
    151 };
    152 
    153 Promise.resolve(thenable);
    154 `);
    155 
    156  Cu.nukeSandbox(sandbox);
    157 
    158  // Drain the job queue, to make sure we hit dead object error inside the
    159  // thenable job.
    160  await Promise.resolve(10);
    161 
    162  equal(
    163    Services.console.getMessageArray().find(x => {
    164      return x.toString().includes("can't access dead object");
    165    }) !== undefined,
    166    true
    167  );
    168  equal(called, false);
    169 });
    170 
    171 add_task(async function testThenableJobAccessError() {
    172  const sandbox = createSandbox();
    173 
    174  let accessed = false;
    175  sandbox.thenable = {
    176    get then() {
    177      accessed = true;
    178      return undefined;
    179    },
    180  };
    181 
    182  // The following operation silently fails when accessing `then` property.
    183  sandbox.eval(`
    184 var x = typeof thenable.then;
    185 
    186 Promise.resolve(thenable);
    187 `);
    188 
    189  equal(accessed, false);
    190 });
    191 
    192 add_task(async function testBoundThenableJob() {
    193  const sandbox = createSandbox();
    194 
    195  sandbox.eval(`
    196 var resolve = undefined;
    197 var callbackPromise = new Promise(r => { resolve = r; });
    198 var callback = function (v) { resolve(v + 1); };
    199 
    200 var then = function(onFulfilled, onRejected) {
    201  onFulfilled(10);
    202 };
    203 `);
    204 
    205  // Create a bound function where its realm is privileged realm, and
    206  // its target is from sandbox realm.
    207  sandbox.bound_then = Function.prototype.bind.call(sandbox.then, sandbox);
    208 
    209  // Creating a promise thenable job in the following `Promise.resolve` performs
    210  // GetFunctionRealm on the bound thenable.then and fails. The reaction job
    211  // should be created with sandbox realm.
    212  sandbox.eval(`
    213 var thenable = {
    214  then: bound_then,
    215 };
    216 
    217 Promise.resolve(thenable).then(callback);
    218 `);
    219 
    220  const result = await sandbox.callbackPromise;
    221  equal(result, 11);
    222 });