test_precursor_principal.js (11125B)
1 "use strict"; 2 const { TestUtils } = ChromeUtils.importESModule( 3 "resource://testing-common/TestUtils.sys.mjs" 4 ); 5 const { XPCShellContentUtils } = ChromeUtils.importESModule( 6 "resource://testing-common/XPCShellContentUtils.sys.mjs" 7 ); 8 const { ExtensionTestUtils } = ChromeUtils.importESModule( 9 "resource://testing-common/ExtensionXPCShellUtils.sys.mjs" 10 ); 11 12 XPCShellContentUtils.init(this); 13 ExtensionTestUtils.init(this); 14 15 const server = XPCShellContentUtils.createHttpServer({ 16 hosts: ["example.com", "example.org"], 17 }); 18 19 server.registerPathHandler("/static_frames", (request, response) => { 20 response.setHeader("Content-Type", "text/html"); 21 response.write(` 22 <iframe name="same_origin" sandbox="allow-scripts allow-same-origin" src="http://example.com/frame"></iframe> 23 <iframe name="same_origin_sandbox" sandbox="allow-scripts" src="http://example.com/frame"></iframe> 24 <iframe name="cross_origin" sandbox="allow-scripts allow-same-origin" src="http://example.org/frame"></iframe> 25 <iframe name="cross_origin_sandbox" sandbox="allow-scripts" src="http://example.org/frame"></iframe> 26 <iframe name="data_uri" sandbox="allow-scripts allow-same-origin" src="data:text/html,<h1>Data Subframe</h1>"></iframe> 27 <iframe name="data_uri_sandbox" sandbox="allow-scripts" src="data:text/html,<h1>Data Subframe</h1>"></iframe> 28 <iframe name="srcdoc" sandbox="allow-scripts allow-same-origin" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe> 29 <iframe name="srcdoc_sandbox" sandbox="allow-scripts" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe> 30 <iframe name="blank" sandbox="allow-scripts allow-same-origin"></iframe> 31 <iframe name="blank_sandbox" sandbox="allow-scripts"></iframe> 32 <iframe name="redirect_com" sandbox="allow-scripts allow-same-origin" src="/redirect?com"></iframe> 33 <iframe name="redirect_com_sandbox" sandbox="allow-scripts" src="/redirect?com"></iframe> 34 <iframe name="redirect_org" sandbox="allow-scripts allow-same-origin" src="/redirect?org"></iframe> 35 <iframe name="redirect_org_sandbox" sandbox="allow-scripts" src="/redirect?org"></iframe> 36 <iframe name="ext_redirect_com_com" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?com"></iframe> 37 <iframe name="ext_redirect_com_com_sandbox" sandbox="allow-scripts" src="/ext_redirect?com"></iframe> 38 <iframe name="ext_redirect_org_com" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?com"></iframe> 39 <iframe name="ext_redirect_org_com_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?com"></iframe> 40 <iframe name="ext_redirect_com_org" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?org"></iframe> 41 <iframe name="ext_redirect_com_org_sandbox" sandbox="allow-scripts" src="/ext_redirect?org"></iframe> 42 <iframe name="ext_redirect_org_org" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?org"></iframe> 43 <iframe name="ext_redirect_org_org_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?org"></iframe> 44 <iframe name="ext_redirect_com_data" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?data"></iframe> 45 <iframe name="ext_redirect_com_data_sandbox" sandbox="allow-scripts" src="/ext_redirect?data"></iframe> 46 <iframe name="ext_redirect_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?data"></iframe> 47 <iframe name="ext_redirect_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?data"></iframe> 48 49 <!-- XXX(nika): These aren't static as they perform loads dynamically - perhaps consider testing them separately? --> 50 <iframe name="client_replace_org_blank" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?blank"></iframe> 51 <iframe name="client_replace_org_blank_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?blank"></iframe> 52 <iframe name="client_replace_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?data"></iframe> 53 <iframe name="client_replace_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?data"></iframe> 54 `); 55 }); 56 57 server.registerPathHandler("/frame", (request, response) => { 58 response.setHeader("Content-Type", "text/html"); 59 response.write(`<h1>HTTP Subframe</h1>`); 60 }); 61 62 server.registerPathHandler("/redirect", (request, response) => { 63 let redirect; 64 if (request.queryString == "com") { 65 redirect = "http://example.com/frame"; 66 } else if (request.queryString == "org") { 67 redirect = "http://example.org/frame"; 68 } else { 69 response.setStatusLine(request.httpVersion, 404, "Not found"); 70 return; 71 } 72 73 response.setStatusLine(request.httpVersion, 302, "Found"); 74 response.setHeader("Location", redirect); 75 }); 76 77 server.registerPathHandler("/client_replace", (request, response) => { 78 let redirect; 79 if (request.queryString == "blank") { 80 redirect = "about:blank"; 81 } else if (request.queryString == "data") { 82 redirect = "data:text/html,<h1>Data Subframe</h1>"; 83 } else { 84 response.setStatusLine(request.httpVersion, 404, "Not found"); 85 return; 86 } 87 88 response.setHeader("Content-Type", "text/html"); 89 response.write(` 90 <script> 91 window.location.replace(${JSON.stringify(redirect)}); 92 </script> 93 `); 94 }); 95 96 add_task(async function sandboxed_precursor() { 97 // Bug 1725345: Make XPCShellContentUtils.createHttpServer support https 98 Services.prefs.setBoolPref("dom.security.https_first", false); 99 100 let extension = await ExtensionTestUtils.loadExtension({ 101 manifest: { 102 permissions: ["webRequest", "webRequestBlocking", "<all_urls>"], 103 }, 104 background() { 105 // eslint-disable-next-line no-undef 106 browser.webRequest.onBeforeRequest.addListener( 107 details => { 108 let url = new URL(details.url); 109 if (!url.pathname.includes("ext_redirect")) { 110 return {}; 111 } 112 113 let redirectUrl; 114 if (url.search == "?com") { 115 redirectUrl = "http://example.com/frame"; 116 } else if (url.search == "?org") { 117 redirectUrl = "http://example.org/frame"; 118 } else if (url.search == "?data") { 119 redirectUrl = "data:text/html,<h1>Data Subframe</h1>"; 120 } 121 return { redirectUrl }; 122 }, 123 { urls: ["<all_urls>"] }, 124 ["blocking"] 125 ); 126 }, 127 }); 128 await extension.startup(); 129 130 registerCleanupFunction(async function () { 131 await extension.unload(); 132 }); 133 134 for (let userContextId of [undefined, 1]) { 135 let comURI = Services.io.newURI("http://example.com"); 136 let comPrin = Services.scriptSecurityManager.createContentPrincipal( 137 comURI, 138 { userContextId } 139 ); 140 let orgURI = Services.io.newURI("http://example.org"); 141 let orgPrin = Services.scriptSecurityManager.createContentPrincipal( 142 orgURI, 143 { userContextId } 144 ); 145 146 let page = await XPCShellContentUtils.loadContentPage( 147 "http://example.com/static_frames", 148 { 149 remote: true, 150 remoteSubframes: true, 151 userContextId, 152 } 153 ); 154 let bc = page.browsingContext; 155 156 ok( 157 bc.currentWindowGlobal.documentPrincipal.equals(comPrin), 158 "toplevel principal matches" 159 ); 160 161 // XXX: This is sketchy as heck, but it's also the easiest way to wait for 162 // the `window.location.replace` loads to finish. 163 await TestUtils.waitForCondition( 164 () => 165 bc.children.every( 166 child => 167 !child.currentWindowGlobal.documentURI.spec.includes( 168 "/client_replace" 169 ) 170 ), 171 "wait for every client_replace global to be replaced" 172 ); 173 174 let principals = {}; 175 for (let child of bc.children) { 176 notEqual(child.name, "", "child frames must have names"); 177 ok(!(child.name in principals), "duplicate child frame name"); 178 principals[child.name] = child.currentWindowGlobal.documentPrincipal; 179 } 180 181 function principal_is(name, expected) { 182 let principal = principals[name]; 183 info(`${name} = ${principal.origin}`); 184 ok(principal.equals(expected), `${name} is correct`); 185 } 186 function precursor_is(name, precursor) { 187 let principal = principals[name]; 188 info(`${name} = ${principal.origin}`); 189 ok(principal.isNullPrincipal, `${name} is null`); 190 ok( 191 principal.precursorPrincipal.equals(precursor), 192 `${name} has the correct precursor` 193 ); 194 } 195 196 // Basic loads should have the principals or precursor principals for the 197 // document being loaded. 198 principal_is("same_origin", comPrin); 199 precursor_is("same_origin_sandbox", comPrin); 200 201 principal_is("cross_origin", orgPrin); 202 precursor_is("cross_origin_sandbox", orgPrin); 203 204 // Loads of a data: URI should complete with a sandboxed principal based on 205 // the principal which tried to perform the load. 206 precursor_is("data_uri", comPrin); 207 precursor_is("data_uri_sandbox", comPrin); 208 209 // Loads which inherit principals, such as srcdoc an about:blank loads, 210 // should also inherit sandboxed precursor principals. 211 principal_is("srcdoc", comPrin); 212 precursor_is("srcdoc_sandbox", comPrin); 213 214 principal_is("blank", comPrin); 215 precursor_is("blank_sandbox", comPrin); 216 217 // Redirects shouldn't interfere with the final principal, and it should be 218 // based only on the final URI. 219 principal_is("redirect_com", comPrin); 220 precursor_is("redirect_com_sandbox", comPrin); 221 222 principal_is("redirect_org", orgPrin); 223 precursor_is("redirect_org_sandbox", orgPrin); 224 225 // Extension redirects should act like normal redirects, and still resolve 226 // with the principal or sandboxed principal of the final URI. 227 principal_is("ext_redirect_com_com", comPrin); 228 precursor_is("ext_redirect_com_com_sandbox", comPrin); 229 230 principal_is("ext_redirect_com_org", orgPrin); 231 precursor_is("ext_redirect_com_org_sandbox", orgPrin); 232 233 principal_is("ext_redirect_org_com", comPrin); 234 precursor_is("ext_redirect_org_com_sandbox", comPrin); 235 236 principal_is("ext_redirect_org_org", orgPrin); 237 precursor_is("ext_redirect_org_org_sandbox", orgPrin); 238 239 // When an extension redirects to a data: URI, we use the last non-data: URI 240 // in the chain as the precursor principal. 241 // FIXME: This should perhaps use the extension's principal instead? 242 precursor_is("ext_redirect_com_data", comPrin); 243 precursor_is("ext_redirect_com_data_sandbox", comPrin); 244 245 precursor_is("ext_redirect_org_data", orgPrin); 246 precursor_is("ext_redirect_org_data_sandbox", orgPrin); 247 248 // Check that navigations triggred by script within the frames will have the 249 // correct behaviour when navigating to blank and data URIs. 250 principal_is("client_replace_org_blank", orgPrin); 251 precursor_is("client_replace_org_blank_sandbox", orgPrin); 252 253 precursor_is("client_replace_org_data", orgPrin); 254 precursor_is("client_replace_org_data_sandbox", orgPrin); 255 256 await page.close(); 257 } 258 Services.prefs.clearUserPref("dom.security.https_first"); 259 });