diff options
Diffstat (limited to 'toolkit/components/antitracking/test/browser/browser_networkIsolation.js')
-rw-r--r-- | toolkit/components/antitracking/test/browser/browser_networkIsolation.js | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/test/browser/browser_networkIsolation.js b/toolkit/components/antitracking/test/browser/browser_networkIsolation.js new file mode 100644 index 0000000000..6a4b4f7653 --- /dev/null +++ b/toolkit/components/antitracking/test/browser/browser_networkIsolation.js @@ -0,0 +1,204 @@ +function isIsolated(key) { + return key.charAt(7) == "i"; +} + +function hasTopWindowOrigin(key, origin) { + let tokenAtEnd = `{{${origin}}}`; + let endPart = key.slice(-tokenAtEnd.length); + return endPart == tokenAtEnd; +} + +function hasAnyTopWindowOrigin(key) { + return !!key.match(/{{[^}]+}}/); +} + +function altSvcCacheKeyIsolated(parsed) { + return parsed.length > 5 && parsed[5] == "I"; +} + +function altSvcTopWindowOrigin(key) { + let index = -1; + for (let i = 0; i < 6; ++i) { + index = key.indexOf(":", index + 1); + } + let indexEnd = key.indexOf("|", index + 1); + return key.substring(index + 1, indexEnd); +} + +const gHttpHandler = Cc["@mozilla.org/network/protocol;1?name=http"].getService( + Ci.nsIHttpProtocolHandler +); + +add_task(async function() { + info("Starting tlsSessionTickets test"); + + await SpecialPowers.flushPrefEnv(); + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.cache.disk.enable", false], + ["browser.cache.memory.enable", false], + [ + "network.cookie.cookieBehavior", + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, + ], + ["network.http.altsvc.proxy_checks", false], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.pbmode.enabled", false], + ["privacy.trackingprotection.annotate_channels", true], + ["privacy.partition.network_state", false], + ], + }); + + await UrlClassifierTestUtils.addTestTrackers(); + + info("Creating a new tab"); + let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + const trackingURL = + "https://tlsresumptiontest.example.org/browser/toolkit/components/antitracking/test/browser/empty-altsvc.js"; + const topWindowOrigin = TEST_DOMAIN.replace(/\/$/, ""); + const kTopic = "http-on-examine-response"; + + let resumedState = []; + let hashKeys = []; + + function observer(subject, topic, data) { + if (topic != kTopic) { + return; + } + + subject.QueryInterface(Ci.nsIChannel); + if (subject.URI.spec != trackingURL) { + return; + } + + resumedState.push( + subject.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo).resumed + ); + hashKeys.push( + subject.QueryInterface(Ci.nsIHttpChannelInternal).connectionInfoHashKey + ); + } + + Services.obs.addObserver(observer, kTopic); + registerCleanupFunction(() => Services.obs.removeObserver(observer, kTopic)); + + function checkAltSvcCache(expectedCount, expectedIsolated) { + let arr = gHttpHandler.altSvcCacheKeys; + is( + arr.length, + expectedCount, + "Found the expected number of items in the cache" + ); + for (let i = 0; i < arr.length; ++i) { + let key = arr[i]; + let parsed = key.split(":"); + if (altSvcCacheKeyIsolated(parsed)) { + ok(expectedIsolated[i], "We expected to find an isolated item"); + is( + altSvcTopWindowOrigin(key), + topWindowOrigin, + "Expected top window origin found in the Alt-Svc cache key" + ); + } else { + ok(!expectedIsolated[i], "We expected to find a non-isolated item"); + } + } + } + + checkAltSvcCache(0, []); + + info("Loading tracking scripts and tracking images"); + await SpecialPowers.spawn(browser, [{ trackingURL }], async function(obj) { + let src = content.document.createElement("script"); + let p = new content.Promise(resolve => { + src.onload = resolve; + }); + content.document.body.appendChild(src); + src.src = obj.trackingURL; + await p; + }); + + checkAltSvcCache(1, [true]); + + // Load our tracking URL two more times, but this time in the first-party context. + // The TLS session should be resumed the second time here. + await fetch(trackingURL); + // Wait a little bit before issuing the second load to ensure both don't happen + // at the same time. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(resolve => setTimeout(resolve, 10)); + + checkAltSvcCache(2, [false, true]); + await fetch(trackingURL).then(() => { + checkAltSvcCache(2, [false, true]); + + is( + resumedState.length, + 3, + "We should have observed 3 loads for " + trackingURL + ); + ok(!resumedState[0], "The first load should NOT have been resumed"); + ok(!resumedState[1], "The second load should NOT have been resumed"); + ok(resumedState[2], "The third load SHOULD have been resumed"); + + // We also verify that the hashKey of the first connection is different to + // both the second and third connections, and that the hashKey of the + // second and third connections are the same. The reason why this check is + // done is that the private bit on the connection info object is used to + // construct the hash key, so when the connection is isolated because it + // comes from a third-party tracker context, its hash key must be + // different. + is( + hashKeys.length, + 3, + "We should have observed 3 loads for " + trackingURL + ); + is(hashKeys[1], hashKeys[2], "The second and third hash keys should match"); + isnot( + hashKeys[0], + hashKeys[1], + "The first and second hash keys should not match" + ); + + ok(isIsolated(hashKeys[0]), "The first connection must have been isolated"); + ok( + !isIsolated(hashKeys[1]), + "The second connection must not have been isolated" + ); + ok( + !isIsolated(hashKeys[2]), + "The third connection must not have been isolated" + ); + ok( + hasTopWindowOrigin(hashKeys[0], topWindowOrigin), + "The first connection must be bound to its top-level window" + ); + ok( + !hasAnyTopWindowOrigin(hashKeys[1]), + "The second connection must not be bound to a top-level window" + ); + ok( + !hasAnyTopWindowOrigin(hashKeys[2]), + "The third connection must not be bound to a top-level window" + ); + }); + + info("Removing the tab"); + BrowserTestUtils.removeTab(tab); + + UrlClassifierTestUtils.cleanupTestTrackers(); +}); + +add_task(async function() { + info("Cleaning up."); + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => + resolve() + ); + }); +}); |