#!/usr/bin/env python # -*- coding: UTF-8 -*- import unittest import pbtest from functools import partial from pbtest import retry_until class FingerprintingTest(pbtest.PBSeleniumTest): """Tests to make sure fingerprinting detection works as expected.""" def detected_fingerprinting(self, domain): return self.js("""let tracker_origin = window.getBaseDomain("{}"); let tabData = chrome.extension.getBackgroundPage().badger.tabData; return ( Object.keys(tabData).some(tab_id => {{ let fpData = tabData[tab_id].fpData; return fpData && fpData.hasOwnProperty(tracker_origin) && fpData[tracker_origin].canvas && fpData[tracker_origin].canvas.fingerprinting === true; }}) );""".format(domain)) def detected_tracking(self, domain, page_url): return self.js("""let tracker_origin = window.getBaseDomain("{}"), site_origin = window.getBaseDomain((new URI("{}")).host), map = chrome.extension.getBackgroundPage().badger.storage.snitch_map.getItemClones(); return ( map.hasOwnProperty(tracker_origin) && map[tracker_origin].indexOf(site_origin) != -1 );""".format(domain, page_url)) def get_fillText_source(self): return self.js(""" const canvas = document.getElementById("writetome"); const ctx = canvas.getContext("2d"); return ctx.fillText.toString(); """) def setUp(self): # enable local learning self.load_url(self.options_url) self.wait_for_script("return window.OPTIONS_INITIALIZED") self.find_el_by_css('#local-learning-checkbox').click() # TODO can fail because our content script runs too late: https://crbug.com/478183 @pbtest.repeat_if_failed(3) def test_canvas_fingerprinting_detection(self): FIXTURE_URL = ( "https://efforg.github.io/privacybadger-test-fixtures/html/" "fingerprinting.html" ) FINGERPRINTING_DOMAIN = "cdn.jsdelivr.net" # clear pre-trained/seed tracker data self.load_url(self.options_url) self.js("chrome.extension.getBackgroundPage().badger.storage.clearTrackerData();") # visit the page self.load_url(FIXTURE_URL) # now open a new window (to avoid clearing badger.tabData) # and verify results self.open_window() # check that we detected the fingerprinting domain as a tracker self.load_url(self.options_url) # TODO unnecessary retrying? self.assertTrue( retry_until(partial(self.detected_tracking, FINGERPRINTING_DOMAIN, FIXTURE_URL)), "Canvas fingerprinting resource was detected as a tracker.") # check that we detected canvas fingerprinting self.assertTrue( self.detected_fingerprinting(FINGERPRINTING_DOMAIN), "Canvas fingerprinting resource was detected as a fingerprinter." ) # Privacy Badger overrides a few functions on canvas contexts to check for fingerprinting. # In previous versions, it would restore the native function after a single call. Unfortunately, # this would wipe out polyfills that had also overridden the same functions, such as the very # popular "hidpi-canvas-polyfill". def test_canvas_polyfill_clobbering(self): FIXTURE_URL = ( "https://efforg.github.io/privacybadger-test-fixtures/html/" "fingerprinting_canvas_hidpi.html" ) # visit the page self.load_url(FIXTURE_URL) # check that we did not restore the native function (should be hipdi polyfill) self.assertNotIn("[native code]", self.get_fillText_source(), "Canvas context fillText is not native version (polyfill has been retained)." ) if __name__ == "__main__": unittest.main()