browser_referrer_disallow_cross_site_relaxing.js (13691B)
1 /** 2 * Bug 1720294 - Testing disallow relaxing default referrer policy for 3 * cross-site requests. 4 */ 5 6 "use strict"; 7 8 requestLongerTimeout(6); 9 10 const TEST_DOMAIN = "https://example.com/"; 11 const TEST_SAME_SITE_DOMAIN = "https://test1.example.com/"; 12 const TEST_SAME_SITE_DOMAIN_HTTP = "http://test1.example.com/"; 13 const TEST_CROSS_SITE_DOMAIN = "https://test1.example.org/"; 14 const TEST_CROSS_SITE_DOMAIN_HTTP = "http://test1.example.org/"; 15 16 const TEST_PATH = "browser/dom/security/test/referrer-policy/"; 17 18 const TEST_PAGE = `${TEST_DOMAIN}${TEST_PATH}referrer_page.sjs`; 19 const TEST_SAME_SITE_PAGE = `${TEST_SAME_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`; 20 const TEST_SAME_SITE_PAGE_HTTP = `${TEST_SAME_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`; 21 const TEST_CROSS_SITE_PAGE = `${TEST_CROSS_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`; 22 const TEST_CROSS_SITE_PAGE_HTTP = `${TEST_CROSS_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`; 23 24 const REFERRER_FULL = 0; 25 const REFERRER_ORIGIN = 1; 26 const REFERRER_NONE = 2; 27 28 function getExpectedReferrer(referrer, type) { 29 let res; 30 31 switch (type) { 32 case REFERRER_FULL: 33 res = referrer; 34 break; 35 case REFERRER_ORIGIN: { 36 let url = new URL(referrer); 37 res = `${url.origin}/`; 38 break; 39 } 40 case REFERRER_NONE: 41 res = ""; 42 break; 43 default: 44 ok(false, "unknown type"); 45 } 46 47 return res; 48 } 49 50 async function verifyResultInPage(browser, expected) { 51 await SpecialPowers.spawn(browser, [expected], value => { 52 is(content.document.referrer, value, "The document.referrer is correct."); 53 54 let result = content.document.getElementById("result"); 55 is(result.textContent, value, "The referer header is correct"); 56 }); 57 } 58 59 function getExpectedConsoleMessage(expected, isPrefOn, url) { 60 let msg; 61 62 if (isPrefOn) { 63 msg = 64 "Referrer Policy: Ignoring the less restricted referrer policy “" + 65 expected + 66 "” for the cross-site request: " + 67 url; 68 } else { 69 msg = 70 "Referrer Policy: Less restricted policies, including " + 71 "‘no-referrer-when-downgrade’, ‘origin-when-cross-origin’ and " + 72 "‘unsafe-url’, will be ignored soon for the cross-site request: " + 73 url; 74 } 75 76 return msg; 77 } 78 79 function createConsoleMessageVerificationPromise(expected, isPrefOn, url) { 80 if (!expected) { 81 return Promise.resolve(); 82 } 83 84 return new Promise(resolve => { 85 let listener = { 86 observe(msg) { 87 let message = msg.QueryInterface(Ci.nsIScriptError); 88 89 if (message.category.startsWith("Security")) { 90 is( 91 message.errorMessage, 92 getExpectedConsoleMessage(expected, isPrefOn, url), 93 "The console message is correct." 94 ); 95 Services.console.unregisterListener(listener); 96 resolve(); 97 } 98 }, 99 }; 100 101 Services.console.registerListener(listener); 102 }); 103 } 104 105 function verifyNoConsoleMessage() { 106 // Verify that there is no referrer policy console message. 107 let allMessages = Services.console.getMessageArray(); 108 109 for (let msg of allMessages) { 110 let message = msg.QueryInterface(Ci.nsIScriptError); 111 if ( 112 message.category.startsWith("Security") && 113 message.errorMessage.startsWith("Referrer Policy:") 114 ) { 115 ok(false, "There should be no console message for referrer policy."); 116 } 117 } 118 } 119 120 const TEST_CASES = [ 121 // Testing that the referrer policy can be overridden with less restricted 122 // policy in the same-origin scenario. 123 { 124 policy: "unsafe-url", 125 referrer: TEST_PAGE, 126 test_url: TEST_PAGE, 127 expect: REFERRER_FULL, 128 original: REFERRER_FULL, 129 }, 130 // Testing that the referrer policy can be overridden with less restricted 131 // policy in the same-site scenario. 132 { 133 policy: "unsafe-url", 134 referrer: TEST_PAGE, 135 test_url: TEST_SAME_SITE_PAGE, 136 expect: REFERRER_FULL, 137 original: REFERRER_FULL, 138 }, 139 { 140 policy: "no-referrer-when-downgrade", 141 referrer: TEST_PAGE, 142 test_url: TEST_SAME_SITE_PAGE, 143 expect: REFERRER_FULL, 144 original: REFERRER_FULL, 145 }, 146 { 147 policy: "origin-when-cross-origin", 148 referrer: TEST_PAGE, 149 test_url: TEST_SAME_SITE_PAGE_HTTP, 150 expect: REFERRER_ORIGIN, 151 original: REFERRER_ORIGIN, 152 }, 153 // Testing that the referrer policy cannot be overridden with less restricted 154 // policy in the cross-site scenario. 155 { 156 policy: "unsafe-url", 157 referrer: TEST_PAGE, 158 test_url: TEST_CROSS_SITE_PAGE, 159 expect: REFERRER_ORIGIN, 160 expect_console: "unsafe-url", 161 original: REFERRER_FULL, 162 }, 163 { 164 policy: "no-referrer-when-downgrade", 165 referrer: TEST_PAGE, 166 test_url: TEST_CROSS_SITE_PAGE, 167 expect: REFERRER_ORIGIN, 168 expect_console: "no-referrer-when-downgrade", 169 original: REFERRER_FULL, 170 }, 171 { 172 policy: "origin-when-cross-origin", 173 referrer: TEST_PAGE, 174 test_url: TEST_CROSS_SITE_PAGE_HTTP, 175 expect: REFERRER_NONE, 176 expect_console: "origin-when-cross-origin", 177 original: REFERRER_ORIGIN, 178 }, 179 // Testing that the referrer policy can still be overridden with more 180 // restricted policy in the cross-site scenario. 181 { 182 policy: "no-referrer", 183 referrer: TEST_PAGE, 184 test_url: TEST_CROSS_SITE_PAGE, 185 expect: REFERRER_NONE, 186 original: REFERRER_NONE, 187 }, 188 ]; 189 190 add_setup(async function () { 191 await SpecialPowers.pushPrefEnv({ 192 set: [ 193 // Disable mixed content blocking to be able to test downgrade scenario. 194 ["security.mixed_content.block_active_content", false], 195 // Disable https-first since we are testing http and https referrers 196 ["dom.security.https_first", false], 197 ["browser.safebrowsing.only_top_level", false], 198 ], 199 }); 200 }); 201 202 async function runTestIniFrame(gBrowser, enabled, expectNoConsole) { 203 await BrowserTestUtils.withNewTab( 204 { gBrowser, url: "about:blank" }, 205 async browser => { 206 for (let type of ["meta", "header"]) { 207 for (let test of TEST_CASES) { 208 info(`Test iframe: ${test.toSource()}`); 209 let referrerURL = `${test.referrer}?${type}=${test.policy}`; 210 let expected = enabled 211 ? getExpectedReferrer(referrerURL, test.expect) 212 : getExpectedReferrer(referrerURL, test.original); 213 214 let expected_console = expectNoConsole 215 ? undefined 216 : test.expect_console; 217 218 Services.console.reset(); 219 220 BrowserTestUtils.startLoadingURIString(browser, referrerURL); 221 await BrowserTestUtils.browserLoaded(browser, false, referrerURL); 222 223 let iframeURL = test.test_url + "?show"; 224 225 let consolePromise = createConsoleMessageVerificationPromise( 226 expected_console, 227 enabled, 228 iframeURL 229 ); 230 // Create an iframe and load the url. 231 let bc = await SpecialPowers.spawn( 232 browser, 233 [iframeURL], 234 async url => { 235 let iframe = content.document.createElement("iframe"); 236 let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load"); 237 iframe.src = url; 238 content.document.body.appendChild(iframe); 239 240 await loadPromise; 241 242 return iframe.browsingContext; 243 } 244 ); 245 246 await verifyResultInPage(bc, expected); 247 await consolePromise; 248 if (!expected_console) { 249 verifyNoConsoleMessage(); 250 } 251 } 252 } 253 } 254 ); 255 } 256 257 async function runTestForLinkClick(gBrowser, enabled, expectNoConsole) { 258 await BrowserTestUtils.withNewTab( 259 { gBrowser, url: "about:blank" }, 260 async browser => { 261 for (let type of ["meta", "header"]) { 262 for (let test of TEST_CASES) { 263 info(`Test link click: ${test.toSource()}`); 264 let referrerURL = `${test.referrer}?${type}=${test.policy}`; 265 let expected = enabled 266 ? getExpectedReferrer(referrerURL, test.expect) 267 : getExpectedReferrer(referrerURL, test.original); 268 269 let expected_console = expectNoConsole 270 ? undefined 271 : test.expect_console; 272 273 Services.console.reset(); 274 275 BrowserTestUtils.startLoadingURIString(browser, referrerURL); 276 await BrowserTestUtils.browserLoaded(browser, false, referrerURL); 277 278 let linkURL = test.test_url + "?show"; 279 280 let consolePromise = createConsoleMessageVerificationPromise( 281 expected_console, 282 enabled, 283 linkURL 284 ); 285 286 // Create the promise to wait for the navigation finishes. 287 let loadedPromise = BrowserTestUtils.browserLoaded( 288 browser, 289 false, 290 linkURL 291 ); 292 293 // Generate the link and click it to navigate. 294 await SpecialPowers.spawn(browser, [linkURL], async url => { 295 let link = content.document.createElement("a"); 296 link.textContent = "Link"; 297 link.setAttribute("href", url); 298 299 content.document.body.appendChild(link); 300 link.click(); 301 }); 302 303 await loadedPromise; 304 305 await verifyResultInPage(browser, expected); 306 await consolePromise; 307 if (!expected_console) { 308 verifyNoConsoleMessage(); 309 } 310 } 311 } 312 } 313 ); 314 } 315 316 async function toggleETPForPage(gBrowser, url, toggle) { 317 let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); 318 319 // First, Toggle ETP off for the test page. 320 let browserLoadedPromise = BrowserTestUtils.browserLoaded( 321 tab.linkedBrowser, 322 false, 323 url 324 ); 325 326 if (toggle) { 327 gProtectionsHandler.enableForCurrentPage(); 328 } else { 329 gProtectionsHandler.disableForCurrentPage(); 330 } 331 332 await browserLoadedPromise; 333 BrowserTestUtils.removeTab(tab); 334 } 335 336 add_task(async function test_iframe() { 337 for (let enabled of [true, false]) { 338 await SpecialPowers.pushPrefEnv({ 339 set: [["network.http.referer.disallowCrossSiteRelaxingDefault", enabled]], 340 }); 341 342 await runTestIniFrame(gBrowser, enabled); 343 } 344 }); 345 346 add_task(async function test_iframe_pbmode() { 347 let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); 348 349 for (let enabled of [true, false]) { 350 await SpecialPowers.pushPrefEnv({ 351 set: [ 352 [ 353 "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode", 354 enabled, 355 ], 356 ], 357 }); 358 359 await runTestIniFrame(win.gBrowser, enabled); 360 } 361 362 await BrowserTestUtils.closeWindow(win); 363 }); 364 365 add_task(async function test_link_click() { 366 for (let enabled of [true, false]) { 367 for (let enabled_top of [true, false]) { 368 await SpecialPowers.pushPrefEnv({ 369 set: [ 370 ["network.http.referer.disallowCrossSiteRelaxingDefault", enabled], 371 [ 372 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", 373 enabled_top, 374 ], 375 ], 376 }); 377 378 // We won't show the console message if the strict rule is disabled for 379 // the top navigation. 380 await runTestForLinkClick(gBrowser, enabled && enabled_top, !enabled_top); 381 } 382 } 383 }); 384 385 add_task(async function test_link_click_pbmode() { 386 let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); 387 388 for (let enabled of [true, false]) { 389 for (let enabled_top of [true, false]) { 390 await SpecialPowers.pushPrefEnv({ 391 set: [ 392 [ 393 "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode", 394 enabled, 395 ], 396 [ 397 "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode.top_navigation", 398 enabled_top, 399 ], 400 // Disable https first mode for private browsing mode to test downgrade 401 // cases. 402 ["dom.security.https_first_pbm", false], 403 ], 404 }); 405 406 // We won't show the console message if the strict rule is disabled for 407 // the top navigation in the private browsing window. 408 await runTestForLinkClick( 409 win.gBrowser, 410 enabled && enabled_top, 411 !enabled_top 412 ); 413 } 414 } 415 416 await BrowserTestUtils.closeWindow(win); 417 }); 418 419 add_task(async function test_iframe_etp_toggle_off() { 420 await SpecialPowers.pushPrefEnv({ 421 set: [["network.http.referer.disallowCrossSiteRelaxingDefault", true]], 422 }); 423 424 // Open a new tab for the test page and toggle ETP off. 425 await toggleETPForPage(gBrowser, TEST_PAGE, false); 426 427 // Run the test to see if the protection is disabled. We won't send console 428 // message if the protection was disabled by the ETP toggle. 429 await runTestIniFrame(gBrowser, false, true); 430 431 // toggle ETP on again. 432 await toggleETPForPage(gBrowser, TEST_PAGE, true); 433 434 // Run the test again to see if the protection is enabled. 435 await runTestIniFrame(gBrowser, true); 436 }); 437 438 add_task(async function test_link_click_etp_toggle_off() { 439 await SpecialPowers.pushPrefEnv({ 440 set: [ 441 ["network.http.referer.disallowCrossSiteRelaxingDefault", true], 442 [ 443 "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", 444 true, 445 ], 446 ], 447 }); 448 449 // Toggle ETP off for the cross site. Note that the cross site is the place 450 // where we test against the ETP permission for top navigation. 451 await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, false); 452 453 // Run the test to see if the protection is disabled. We won't send console 454 // message if the protection was disabled by the ETP toggle. 455 await runTestForLinkClick(gBrowser, false, true); 456 457 // toggle ETP on again. 458 await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, true); 459 460 // Run the test again to see if the protection is enabled. 461 await runTestForLinkClick(gBrowser, true); 462 });