diff options
Diffstat (limited to 'tests/selenium/dnt_test.py')
-rw-r--r-- | tests/selenium/dnt_test.py | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/tests/selenium/dnt_test.py b/tests/selenium/dnt_test.py new file mode 100644 index 0000000..fb3fdf8 --- /dev/null +++ b/tests/selenium/dnt_test.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +import json +import unittest + +import pbtest + +from functools import partial + +from selenium.common.exceptions import NoSuchElementException + +from pbtest import retry_until + + +class DntTest(pbtest.PBSeleniumTest): + """Tests to make sure DNT policy checking works as expected.""" + + CHECK_FOR_DNT_POLICY_JS = ( + "chrome.extension.getBackgroundPage()." + "badger.checkForDNTPolicy(" + " arguments[0]," + " r => window.DNT_CHECK_RESULT = r" + ");" + ) + + # TODO switch to non-delayed version + # https://gist.github.com/ghostwords/9fc6900566a2f93edd8e4a1e48bbaa28 + # once race condition (https://crbug.com/478183) is fixed + NAVIGATOR_DNT_TEST_URL = ( + "https://efforg.github.io/privacybadger-test-fixtures/html/" + "navigator_donottrack_delayed.html" + ) + + def get_first_party_headers(self, url): + self.load_url(url) + + text = self.driver.find_element_by_tag_name('body').text + + try: + headers = json.loads(text)['headers'] + except ValueError: + print("\nFailed to parse JSON from {}".format(repr(text))) + return None + + return headers + + def domain_was_recorded(self, domain): + return self.js( + "return (Object.keys(" + " chrome.extension.getBackgroundPage()." + " badger.storage.action_map.getItemClones()" + ").indexOf(arguments[0]) != -1);", + domain + ) + + def domain_was_detected(self, domain): + return self.js( + "return (Object.keys(chrome.extension.getBackgroundPage().badger.tabData).some(tab_id => {" + " let origins = chrome.extension.getBackgroundPage().badger.tabData[tab_id].origins;" + " return origins.hasOwnProperty(arguments[0]);" + "}));", + domain + ) + + def domain_was_blocked(self, domain): + return self.js( + "return (Object.keys(chrome.extension.getBackgroundPage().badger.tabData).some(tab_id => {" + " let origins = chrome.extension.getBackgroundPage().badger.tabData[tab_id].origins;" + " return (" + " origins.hasOwnProperty(arguments[0]) &&" + " chrome.extension.getBackgroundPage().constants.BLOCKED_ACTIONS.has(origins[arguments[0]])" + " );" + "}));", + domain + ) + + @pbtest.repeat_if_failed(3) + def test_dnt_policy_check_should_happen_for_blocked_domains(self): + PAGE_URL = ( + "https://efforg.github.io/privacybadger-test-fixtures/html/" + "dnt.html" + ) + DNT_DOMAIN = "www.eff.org" + + # mark a DNT-compliant domain for blocking + self.block_domain(DNT_DOMAIN) + + # visit a page that loads a resource from that DNT-compliant domain + self.open_window() + self.load_url(PAGE_URL) + + # switch back to Badger's options page + self.switch_to_window_with_url(self.options_url) + + # verify that the domain is blocked + self.assertTrue(self.domain_was_detected(DNT_DOMAIN), + msg="Domain should have been detected.") + self.assertTrue(self.domain_was_blocked(DNT_DOMAIN), + msg="DNT-compliant resource should have been blocked at first.") + + def reload_and_see_if_unblocked(): + # switch back to the page with the DNT-compliant resource + self.switch_to_window_with_url(PAGE_URL) + + # reload it + self.load_url(PAGE_URL) + + # switch back to Badger's options page + self.switch_to_window_with_url(self.options_url) + + return ( + self.domain_was_detected(DNT_DOMAIN) and + self.domain_was_blocked(DNT_DOMAIN) + ) + + # verify that the domain is allowed + was_blocked = retry_until( + reload_and_see_if_unblocked, + tester=lambda x: not x, + msg="Waiting a bit for DNT check to complete and retrying ...") + + self.assertFalse(was_blocked, + msg="DNT-compliant resource should have gotten unblocked.") + + def test_dnt_policy_check_should_not_set_cookies(self): + TEST_DOMAIN = "dnt-test.trackersimulator.org" + TEST_URL = "https://{}/".format(TEST_DOMAIN) + + # verify that the domain itself doesn't set cookies + self.load_url(TEST_URL) + self.assertEqual(len(self.driver.get_cookies()), 0, + "No cookies initially") + + # directly visit a DNT policy URL known to set cookies + self.load_url(TEST_URL + ".well-known/dnt-policy.txt") + self.assertEqual(len(self.driver.get_cookies()), 1, + "DNT policy URL set a cookie") + + # verify we got a cookie + self.load_url(TEST_URL) + self.assertEqual(len(self.driver.get_cookies()), 1, + "We still have just one cookie") + + # clear cookies and verify + self.driver.delete_all_cookies() + self.load_url(TEST_URL) + self.assertEqual(len(self.driver.get_cookies()), 0, + "No cookies again") + + self.load_url(self.options_url) + # perform a DNT policy check + self.js(DntTest.CHECK_FOR_DNT_POLICY_JS, TEST_DOMAIN) + # wait until checkForDNTPolicy completed + self.wait_for_script("return window.DNT_CHECK_RESULT === false") + + # check that we didn't get cookied by the DNT URL + self.load_url(TEST_URL) + self.assertEqual(len(self.driver.get_cookies()), 0, + "Shouldn't have any cookies after the DNT check") + + def test_dnt_policy_check_should_not_send_cookies(self): + TEST_DOMAIN = "dnt-request-cookies-test.trackersimulator.org" + TEST_URL = "https://{}/".format(TEST_DOMAIN) + + # directly visit a DNT policy URL known to set cookies + self.load_url(TEST_URL + ".well-known/dnt-policy.txt") + self.assertEqual(len(self.driver.get_cookies()), 1, + "DNT policy URL set a cookie") + + # how to check we didn't send a cookie along with request? + # the DNT policy URL used by this test returns "cookies=X" + # where X is the number of cookies it got + # MEGAHACK: make sha1 of "cookies=0" a valid DNT hash + self.load_url(self.options_url) + self.js( + "chrome.extension.getBackgroundPage()." + "badger.storage.updateDntHashes({" + " 'cookies=0 test policy': 'f63ee614ebd77f8634b92633c6bb809a64b9a3d7'" + "});" + ) + + # perform a DNT policy check + self.js(DntTest.CHECK_FOR_DNT_POLICY_JS, TEST_DOMAIN) + # wait until checkForDNTPolicy completed + self.wait_for_script("return typeof window.DNT_CHECK_RESULT != 'undefined';") + # get the result + result = self.js("return window.DNT_CHECK_RESULT;") + self.assertTrue(result, "No cookies were sent") + + def test_should_not_record_nontracking_domains(self): + FIXTURE_URL = ( + "https://efforg.github.io/privacybadger-test-fixtures/html/" + "recording_nontracking_domains.html" + ) + TRACKING_DOMAIN = "dnt-request-cookies-test.trackersimulator.org" + NON_TRACKING_DOMAIN = "www.eff.org" + + # clear pre-trained/seed tracker data + self.load_url(self.options_url) + self.js("chrome.extension.getBackgroundPage().badger.storage.clearTrackerData();") + + # enable local learning + self.wait_for_script("return window.OPTIONS_INITIALIZED") + self.find_el_by_css('#local-learning-checkbox').click() + + # visit a page containing two third-party resources, + # one from a cookie-tracking domain + # and one from a non-tracking domain + self.load_url(FIXTURE_URL) + + # verify both domains are present on the page + try: + selector = "iframe[src*='%s']" % TRACKING_DOMAIN + self.driver.find_element_by_css_selector(selector) + except NoSuchElementException: + self.fail("Unable to find the tracking domain on the page") + try: + selector = "img[src*='%s']" % NON_TRACKING_DOMAIN + self.driver.find_element_by_css_selector(selector) + except NoSuchElementException: + self.fail("Unable to find the non-tracking domain on the page") + + self.load_url(self.options_url) + + # verify that the cookie-tracking domain was recorded + self.assertTrue( + self.domain_was_recorded(TRACKING_DOMAIN), + "Tracking domain should have gotten recorded" + ) + + # verify that the non-tracking domain was not recorded + self.assertFalse( + self.domain_was_recorded(NON_TRACKING_DOMAIN), + "Non-tracking domain should not have gotten recorded" + ) + + def test_first_party_dnt_header(self): + TEST_URL = "https://httpbin.org/get" + headers = retry_until(partial(self.get_first_party_headers, TEST_URL), + times=8) + self.assertTrue(headers is not None, "It seems we failed to get headers") + self.assertIn('Dnt', headers, "DNT header should have been present") + self.assertIn('Sec-Gpc', headers, "GPC header should have been present") + self.assertEqual(headers['Dnt'], "1", + 'DNT header should have been set to "1"') + self.assertEqual(headers['Sec-Gpc'], "1", + 'Sec-Gpc header should have been set to "1"') + + def test_no_dnt_header_when_disabled_on_site(self): + TEST_URL = "https://httpbin.org/get" + self.disable_badger_on_site(TEST_URL) + headers = retry_until(partial(self.get_first_party_headers, TEST_URL), + times=8) + self.assertTrue(headers is not None, "It seems we failed to get headers") + self.assertNotIn('Dnt', headers, "DNT header should have been missing") + self.assertNotIn('Sec-Gpc', headers, "GPC header should have been missing") + + def test_no_dnt_header_when_dnt_disabled(self): + TEST_URL = "https://httpbin.org/get" + + self.load_url(self.options_url) + self.wait_for_script("return window.OPTIONS_INITIALIZED") + self.find_el_by_css('#enable_dnt_checkbox').click() + + headers = retry_until(partial(self.get_first_party_headers, TEST_URL), + times=8) + self.assertTrue(headers is not None, "It seems we failed to get headers") + self.assertNotIn('Dnt', headers, "DNT header should have been missing") + self.assertNotIn('Sec-Gpc', headers, "GPC header should have been missing") + + def test_navigator_object(self): + self.load_url(DntTest.NAVIGATOR_DNT_TEST_URL, wait_for_body_text=True) + + self.assertEqual( + self.driver.find_element_by_tag_name('body').text, + 'no tracking (navigator.doNotTrack="1")', + "navigator.DoNotTrack should have been set to \"1\"" + ) + self.assertEqual( + self.js("return navigator.globalPrivacyControl"), + "1", + "navigator.globalPrivacyControl should have been set to \"1\"" + ) + + def test_navigator_unmodified_when_disabled_on_site(self): + self.disable_badger_on_site(DntTest.NAVIGATOR_DNT_TEST_URL) + + self.load_url(DntTest.NAVIGATOR_DNT_TEST_URL, wait_for_body_text=True) + + # navigator.doNotTrack defaults to null in Chrome, "unspecified" in Firefox + self.assertEqual( + self.driver.find_element_by_tag_name('body').text[0:5], + 'unset', + "navigator.DoNotTrack should have been left unset" + ) + self.assertEqual( + self.js("return navigator.globalPrivacyControl"), + None, + "navigator.globalPrivacyControl should have been left unset" + ) + + def test_navigator_unmodified_when_dnt_disabled(self): + self.load_url(self.options_url) + self.wait_for_script("return window.OPTIONS_INITIALIZED") + self.find_el_by_css('#enable_dnt_checkbox').click() + + self.load_url(DntTest.NAVIGATOR_DNT_TEST_URL, wait_for_body_text=True) + + # navigator.doNotTrack defaults to null in Chrome, "unspecified" in Firefox + self.assertEqual( + self.driver.find_element_by_tag_name('body').text[0:5], + 'unset', + "navigator.DoNotTrack should have been left unset" + ) + self.assertEqual( + self.js("return navigator.globalPrivacyControl"), + None, + "navigator.globalPrivacyControl should have been left unset" + ) + + +if __name__ == "__main__": + unittest.main() |