browser_3pcb_expection.js (18393B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { RemoteSettings } = ChromeUtils.importESModule( 8 "resource://services-settings/remote-settings.sys.mjs" 9 ); 10 11 const COLLECTION_NAME = "third-party-cookie-blocking-exempt-urls"; 12 const PREF_NAME = "network.cookie.cookieBehavior.optInPartitioning.skip_list"; 13 14 const FIRST_PARTY_DOMAIN = "example.com"; 15 const THIRD_PARTY_DOMAIN = "example.org"; 16 const ANOTHER_THIRD_PARTY_DOMAIN = "example.net"; 17 18 const FIRST_PARTY_SITE = `https://${FIRST_PARTY_DOMAIN}`; 19 const THIRD_PARTY_SITE = `https://${THIRD_PARTY_DOMAIN}`; 20 const ANOTHER_THIRD_PARTY_SITE = `https://${ANOTHER_THIRD_PARTY_DOMAIN}`; 21 22 const FIRST_PARTY_URL = `${FIRST_PARTY_SITE}/${TEST_PATH}/file_empty.html`; 23 const THIRD_PARTY_URL = `${THIRD_PARTY_SITE}/${TEST_PATH}/file_empty.html`; 24 25 // RemoteSettings collection db. 26 let db; 27 28 /** 29 * Dispatch a RemoteSettings "sync" event. 30 * 31 * @param {object} data - The event's data payload. 32 * @param {object} [data.created] - Records that were created. 33 * @param {object} [data.updated] - Records that were updated. 34 * @param {object} [data.deleted] - Records that were removed. 35 */ 36 async function remoteSettingsSync({ created, updated, deleted }) { 37 await RemoteSettings(COLLECTION_NAME).emit("sync", { 38 data: { 39 created, 40 updated, 41 deleted, 42 }, 43 }); 44 } 45 46 /** 47 * Compare two string arrays ignoring order. 48 * 49 * @param {string[]} arr1 - The first array. 50 * @param {string[]} arr2 - The second array. 51 * @returns {boolean} - Whether the arrays match. 52 */ 53 const strArrayMatches = (arr1, arr2) => 54 arr1.length === arr2.length && 55 arr1.sort().every((value, index) => value === arr2.sort()[index]); 56 57 /** 58 * Wait until the 3pcb allow-list matches the expected state. 59 * 60 * @param {string[]} allowedSiteHosts - (Unordered) host list to match. 61 */ 62 async function waitForAllowListState(expected) { 63 // Ensure the site host exception list has been imported correctly. 64 await BrowserTestUtils.waitForCondition(() => { 65 return strArrayMatches(Services.cookies.testGet3PCBExceptions(), expected); 66 }, "Waiting for exceptions to be imported."); 67 Assert.deepEqual( 68 Services.cookies.testGet3PCBExceptions().sort(), 69 expected.sort(), 70 "Imported the correct site host exceptions" 71 ); 72 } 73 74 /** 75 * A helper function to create the iframe and the nested ABA iframe. 76 * 77 * @param {Browser} browser The browser where the testing is performed. 78 * @param {string} firstPartyURL The first party URL. 79 * @param {string} thirdPartyURL The third party URL. 80 * @returns {Promise} A promise that resolves to the iframe browsing context 81 * and the ABA iframe browsing context. 82 */ 83 async function createNestedIframes(browser, firstPartyURL, thirdPartyURL) { 84 return SpecialPowers.spawn( 85 browser, 86 [firstPartyURL, thirdPartyURL], 87 async (firstPartyURL, thirdPartyURL) => { 88 let iframe = content.document.createElement("iframe"); 89 iframe.src = thirdPartyURL; 90 91 await new Promise(resolve => { 92 iframe.onload = resolve; 93 content.document.body.appendChild(iframe); 94 }); 95 96 let ABABC = await SpecialPowers.spawn( 97 iframe, 98 [firstPartyURL], 99 async firstPartyURL => { 100 let iframe = content.document.createElement("iframe"); 101 iframe.src = firstPartyURL; 102 103 await new Promise(resolve => { 104 iframe.onload = resolve; 105 content.document.body.appendChild(iframe); 106 }); 107 108 return iframe.browsingContext; 109 } 110 ); 111 112 return { iframeBC: iframe.browsingContext, ABABC }; 113 } 114 ); 115 } 116 117 /** 118 * A helper function to set third-party cookies in the third-party iframe and 119 * the ABA iframe. 120 * 121 * @param {Browser} browser The browser where the testing is performed. 122 * @param {CanonicalBrowsingContext} iframeBC The iframe browsing context. 123 * @param {CanonicalBrowsingContext} ABAABC The ABA browsing context. 124 */ 125 async function setThirdPartyCookie(browser, iframeBC, ABABC) { 126 const THIRD_PARTY_FETCH_COOKIE_URL = `${THIRD_PARTY_SITE}/${TEST_PATH}/setFetchCookie.sjs`; 127 128 // Try to set a third-party cookie by fetching from the third-party URL. 129 await SpecialPowers.spawn( 130 browser, 131 [THIRD_PARTY_FETCH_COOKIE_URL], 132 async url => { 133 await content.fetch(url, { credentials: "include" }); 134 } 135 ); 136 137 // Set a third-party cookie in the third-party iframe. 138 await SpecialPowers.spawn(iframeBC, [], async _ => { 139 content.document.cookie = "thirdPartyIframe=value; SameSite=None; Secure;"; 140 }); 141 142 // Set a ABA cookie in the nested iframe. An ABA cookie is also considered 143 // as a third-party cookie. 144 await SpecialPowers.spawn(ABABC, [], async _ => { 145 content.document.cookie = "ABAIframe=value; SameSite=None; Secure;"; 146 }); 147 } 148 149 add_setup(async function () { 150 await SpecialPowers.pushPrefEnv({ 151 set: [["network.cookie.cookieBehavior.optInPartitioning", true]], 152 }); 153 154 // Start with an empty RS collection. 155 db = RemoteSettings(COLLECTION_NAME).db; 156 await db.importChanges({}, Date.now(), [], { clear: true }); 157 }); 158 159 add_task(async function test_3pcb_no_exception() { 160 // Clear cookies before running the test. 161 Services.cookies.removeAll(); 162 163 info("Opening a new tab."); 164 let tab = await BrowserTestUtils.openNewForegroundTab( 165 gBrowser, 166 FIRST_PARTY_URL 167 ); 168 let browser = tab.linkedBrowser; 169 170 info("Creating iframes and setting third-party cookies."); 171 let { iframeBC, ABABC } = await createNestedIframes( 172 browser, 173 FIRST_PARTY_URL, 174 THIRD_PARTY_URL 175 ); 176 await setThirdPartyCookie(browser, iframeBC, ABABC); 177 178 info("Verifying cookies."); 179 // Verify in the iframeBC to ensure no cookie is set. 180 await SpecialPowers.spawn(iframeBC, [], async () => { 181 let cookies = content.document.cookie; 182 is(cookies, "", "No cookies should be set in the iframeBC"); 183 }); 184 185 // Verify in the nested iframe to ensure no cookie is set. 186 await SpecialPowers.spawn(ABABC, [], async () => { 187 let cookies = content.document.cookie; 188 is(cookies, "", "No cookies should be set in the ABA iframe"); 189 }); 190 191 info("Clean up"); 192 BrowserTestUtils.removeTab(tab); 193 }); 194 195 add_task(async function test_3pcb_pref_exception() { 196 // Clear cookies before running the test. 197 Services.cookies.removeAll(); 198 199 await SpecialPowers.pushPrefEnv({ 200 set: [ 201 [ 202 PREF_NAME, 203 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE};${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 204 ], 205 ], 206 }); 207 208 info("Opening a new tab."); 209 let tab = await BrowserTestUtils.openNewForegroundTab( 210 gBrowser, 211 FIRST_PARTY_URL 212 ); 213 let browser = tab.linkedBrowser; 214 215 info("Creating iframes and setting third-party cookies."); 216 let { iframeBC, ABABC } = await createNestedIframes( 217 browser, 218 FIRST_PARTY_URL, 219 THIRD_PARTY_URL 220 ); 221 await setThirdPartyCookie(browser, iframeBC, ABABC); 222 223 info("Verifying cookies."); 224 // Verify in the iframeBC to ensure cookies are set. 225 await SpecialPowers.spawn(iframeBC, [], async () => { 226 let cookies = content.document.cookie; 227 is( 228 cookies, 229 "thirdPartyFetch=value; thirdPartyIframe=value", 230 "Cookies should be set in the iframeBC" 231 ); 232 }); 233 234 // Verify in the nested ABA iframe to ensure no cookie is set. 235 await SpecialPowers.spawn(ABABC, [], async () => { 236 let cookies = content.document.cookie; 237 is( 238 cookies, 239 "ABAIframe=value", 240 "No cookies should be set in the ABA iframe" 241 ); 242 }); 243 BrowserTestUtils.removeTab(tab); 244 245 info("Clear exceptions and verify cookies are still valid"); 246 await SpecialPowers.pushPrefEnv({ 247 set: [[PREF_NAME, ""]], 248 }); 249 250 info("Opening the tab again."); 251 tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_PARTY_URL); 252 browser = tab.linkedBrowser; 253 254 let { iframeBC: iframeBCNew, ABABC: ABABCNew } = await createNestedIframes( 255 browser, 256 FIRST_PARTY_URL, 257 THIRD_PARTY_URL 258 ); 259 260 await SpecialPowers.spawn(iframeBCNew, [], async () => { 261 let cookies = content.document.cookie; 262 is( 263 cookies, 264 "thirdPartyFetch=value; thirdPartyIframe=value", 265 "Cookies should be set in the iframeBC" 266 ); 267 }); 268 await SpecialPowers.spawn(ABABCNew, [], async () => { 269 let cookies = content.document.cookie; 270 is( 271 cookies, 272 "ABAIframe=value", 273 "No cookies should be set in the ABA iframe" 274 ); 275 }); 276 277 info("Clean up"); 278 BrowserTestUtils.removeTab(tab); 279 }); 280 281 add_task(async function test_3pcb_pref_wildcard_exception() { 282 // Clear cookies before running the test. 283 Services.cookies.removeAll(); 284 285 await SpecialPowers.pushPrefEnv({ 286 set: [[PREF_NAME, `*,${THIRD_PARTY_SITE};*,${FIRST_PARTY_SITE}`]], 287 }); 288 289 info("Opening a new tab."); 290 let tab = await BrowserTestUtils.openNewForegroundTab( 291 gBrowser, 292 FIRST_PARTY_URL 293 ); 294 let browser = tab.linkedBrowser; 295 296 info("Creating iframes and setting third-party cookies."); 297 let { iframeBC, ABABC } = await createNestedIframes( 298 browser, 299 FIRST_PARTY_URL, 300 THIRD_PARTY_URL 301 ); 302 await setThirdPartyCookie(browser, iframeBC, ABABC); 303 304 info("Verifying cookies."); 305 // Verify in the iframeBC to ensure cookies are set. 306 await SpecialPowers.spawn(iframeBC, [], async () => { 307 let cookies = content.document.cookie; 308 is( 309 cookies, 310 "thirdPartyFetch=value; thirdPartyIframe=value", 311 "Cookies should be set in the iframeBC" 312 ); 313 }); 314 315 // Verify in the nested ABA iframe to ensure no cookie is set. 316 await SpecialPowers.spawn(ABABC, [], async () => { 317 let cookies = content.document.cookie; 318 is(cookies, "ABAIframe=value", "Cookies should be set in the ABA iframe"); 319 }); 320 BrowserTestUtils.removeTab(tab); 321 322 info("Clear exceptions and verify cookies are still valid"); 323 await SpecialPowers.pushPrefEnv({ 324 set: [[PREF_NAME, ""]], 325 }); 326 327 info("Opening the tab again."); 328 tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_PARTY_URL); 329 browser = tab.linkedBrowser; 330 331 let { iframeBC: iframeBCNew, ABABC: ABABCNew } = await createNestedIframes( 332 browser, 333 FIRST_PARTY_URL, 334 THIRD_PARTY_URL 335 ); 336 337 await SpecialPowers.spawn(iframeBCNew, [], async () => { 338 let cookies = content.document.cookie; 339 is( 340 cookies, 341 "thirdPartyFetch=value; thirdPartyIframe=value", 342 "Cookies should be set in the iframeBC" 343 ); 344 }); 345 await SpecialPowers.spawn(ABABCNew, [], async () => { 346 let cookies = content.document.cookie; 347 is( 348 cookies, 349 "ABAIframe=value", 350 "No cookies should be set in the ABA iframe" 351 ); 352 }); 353 354 info("Clean up"); 355 BrowserTestUtils.removeTab(tab); 356 }); 357 358 add_task(async function test_3pcb_pref_exception_updates() { 359 // Start with an empty pref 360 await SpecialPowers.pushPrefEnv({ 361 set: [[PREF_NAME, ""]], 362 }); 363 364 info("Set initial pref value"); 365 Services.prefs.setStringPref( 366 PREF_NAME, 367 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE};${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}` 368 ); 369 await waitForAllowListState([ 370 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 371 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 372 ]); 373 374 info("Update the pref exception"); 375 Services.prefs.setStringPref( 376 PREF_NAME, 377 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE};${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}` 378 ); 379 await waitForAllowListState([ 380 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE}`, 381 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 382 ]); 383 384 info("Remove one exception"); 385 Services.prefs.setStringPref( 386 PREF_NAME, 387 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE}` 388 ); 389 await waitForAllowListState([ 390 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE}`, 391 ]); 392 393 info("Remove all exceptions"); 394 Services.prefs.setStringPref(PREF_NAME, ""); 395 await waitForAllowListState([]); 396 397 info("Cleanup"); 398 Services.prefs.clearUserPref(PREF_NAME); 399 }); 400 401 add_task(async function test_3pcb_rs_exception() { 402 // Clear cookies before running the test. 403 Services.cookies.removeAll(); 404 405 info("Import RS entries."); 406 let thirdPartyEntry = await db.create({ 407 fpSite: FIRST_PARTY_SITE, 408 tpSite: THIRD_PARTY_SITE, 409 }); 410 let ABAEntry = await db.create({ 411 fpSite: FIRST_PARTY_SITE, 412 tpSite: FIRST_PARTY_SITE, 413 }); 414 await db.importChanges({}, Date.now()); 415 await remoteSettingsSync({ created: [thirdPartyEntry, ABAEntry] }); 416 await waitForAllowListState([ 417 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 418 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 419 ]); 420 421 info("Opening a new tab."); 422 let tab = await BrowserTestUtils.openNewForegroundTab( 423 gBrowser, 424 FIRST_PARTY_URL 425 ); 426 let browser = tab.linkedBrowser; 427 428 info("Creating iframes and setting third-party cookies."); 429 let { iframeBC, ABABC } = await createNestedIframes( 430 browser, 431 FIRST_PARTY_URL, 432 THIRD_PARTY_URL 433 ); 434 await setThirdPartyCookie(browser, iframeBC, ABABC); 435 436 info("Verifying cookies."); 437 // Verify in the iframeBC to ensure cookies are set. 438 await SpecialPowers.spawn(iframeBC, [], async () => { 439 let cookies = content.document.cookie; 440 is( 441 cookies, 442 "thirdPartyFetch=value; thirdPartyIframe=value", 443 "Cookies should be set in the iframeBC" 444 ); 445 }); 446 447 // Verify in the nested ABA iframe to ensure the cookie is set. 448 await SpecialPowers.spawn(ABABC, [], async () => { 449 let cookies = content.document.cookie; 450 is( 451 cookies, 452 "ABAIframe=value", 453 "No cookies should be set in the ABA iframe" 454 ); 455 }); 456 BrowserTestUtils.removeTab(tab); 457 458 info("Clear exceptions and verify cookies are still valid"); 459 await db.delete(thirdPartyEntry.id); 460 await db.delete(ABAEntry.id); 461 await db.importChanges({}, Date.now()); 462 await remoteSettingsSync({ 463 deleted: [thirdPartyEntry, ABAEntry], 464 }); 465 await waitForAllowListState([]); 466 467 info("Opening the tab again."); 468 tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_PARTY_URL); 469 browser = tab.linkedBrowser; 470 471 let { iframeBC: iframeBCNew, ABABC: ABABCNew } = await createNestedIframes( 472 browser, 473 FIRST_PARTY_URL, 474 THIRD_PARTY_URL 475 ); 476 477 await SpecialPowers.spawn(iframeBCNew, [], async () => { 478 let cookies = content.document.cookie; 479 is( 480 cookies, 481 "thirdPartyFetch=value; thirdPartyIframe=value", 482 "Cookies should be set in the iframeBC" 483 ); 484 }); 485 await SpecialPowers.spawn(ABABCNew, [], async () => { 486 let cookies = content.document.cookie; 487 is( 488 cookies, 489 "ABAIframe=value", 490 "No cookies should be set in the ABA iframe" 491 ); 492 }); 493 494 info("Clean up"); 495 BrowserTestUtils.removeTab(tab); 496 await db.clear(); 497 await db.importChanges({}, Date.now()); 498 }); 499 500 add_task(async function test_3pcb_rs_exception_updates() { 501 info("Create the third-party entry and the ABA entry."); 502 let thirdPartyEntry = await db.create({ 503 fpSite: FIRST_PARTY_SITE, 504 tpSite: THIRD_PARTY_SITE, 505 }); 506 let ABAEntry = await db.create({ 507 fpSite: FIRST_PARTY_SITE, 508 tpSite: FIRST_PARTY_SITE, 509 }); 510 await db.importChanges({}, Date.now()); 511 await remoteSettingsSync({ created: [thirdPartyEntry, ABAEntry] }); 512 await waitForAllowListState([ 513 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 514 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 515 ]); 516 517 info("Update third-party entry with a different third-party site."); 518 let thirdPartyEntryUpdated = { ...thirdPartyEntry }; 519 thirdPartyEntryUpdated.tpSite = ANOTHER_THIRD_PARTY_SITE; 520 await db.update(thirdPartyEntry); 521 await db.importChanges({}, Date.now()); 522 await remoteSettingsSync({ 523 updated: [{ old: thirdPartyEntry, new: thirdPartyEntryUpdated }], 524 }); 525 await waitForAllowListState([ 526 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE}`, 527 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 528 ]); 529 530 info("Create another entry and remove the ABA entry."); 531 let anotherThirdPartyEntry = await db.create({ 532 fpSite: ANOTHER_THIRD_PARTY_SITE, 533 tpSite: THIRD_PARTY_SITE, 534 }); 535 await db.delete(ABAEntry.id); 536 await db.importChanges({}, Date.now()); 537 await remoteSettingsSync({ 538 created: [anotherThirdPartyEntry], 539 deleted: [ABAEntry], 540 }); 541 await waitForAllowListState([ 542 `${FIRST_PARTY_SITE},${ANOTHER_THIRD_PARTY_SITE}`, 543 `${ANOTHER_THIRD_PARTY_SITE},${THIRD_PARTY_SITE}`, 544 ]); 545 546 info("Remove all RS entries."); 547 await db.delete(thirdPartyEntryUpdated.id); 548 await db.delete(anotherThirdPartyEntry.id); 549 await db.importChanges({}, Date.now()); 550 await remoteSettingsSync({ 551 deleted: [thirdPartyEntryUpdated, anotherThirdPartyEntry], 552 }); 553 await waitForAllowListState([]); 554 555 info("Clean up"); 556 await db.clear(); 557 await db.importChanges({}, Date.now()); 558 }); 559 560 add_task(async function test_3pcb_rs_precedence_over_pref() { 561 info("Create the third-party entry and the ABA entry."); 562 let thirdPartyEntry = await db.create({ 563 fpSite: FIRST_PARTY_SITE, 564 tpSite: THIRD_PARTY_SITE, 565 }); 566 let ABAEntry = await db.create({ 567 fpSite: FIRST_PARTY_SITE, 568 tpSite: FIRST_PARTY_SITE, 569 }); 570 await db.importChanges({}, Date.now()); 571 await remoteSettingsSync({ created: [thirdPartyEntry, ABAEntry] }); 572 await waitForAllowListState([ 573 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 574 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 575 ]); 576 577 info("Set the duplicate pref exception."); 578 // Verify that we don't introduce duplicate exceptions if we set the same 579 // exception via pref. 580 await SpecialPowers.pushPrefEnv({ 581 set: [ 582 [ 583 PREF_NAME, 584 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE};${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 585 ], 586 ], 587 }); 588 await waitForAllowListState([ 589 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 590 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 591 ]); 592 593 info("Remove the pref exception."); 594 // Verify that the RS exception is still there even if we remove the same 595 // exception via pref. 596 await SpecialPowers.pushPrefEnv({ 597 set: [[PREF_NAME, ""]], 598 }); 599 await waitForAllowListState([ 600 `${FIRST_PARTY_SITE},${THIRD_PARTY_SITE}`, 601 `${FIRST_PARTY_SITE},${FIRST_PARTY_SITE}`, 602 ]); 603 604 info("Clean up"); 605 await db.delete(thirdPartyEntry.id); 606 await db.delete(ABAEntry.id); 607 await db.importChanges({}, Date.now()); 608 await remoteSettingsSync({ 609 deleted: [thirdPartyEntry, ABAEntry], 610 }); 611 await waitForAllowListState([]); 612 await db.clear(); 613 await db.importChanges({}, Date.now()); 614 });