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 });