diff options
Diffstat (limited to 'src/tests/tests/storage.js')
-rw-r--r-- | src/tests/tests/storage.js | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/src/tests/tests/storage.js b/src/tests/tests/storage.js new file mode 100644 index 0000000..cb4042d --- /dev/null +++ b/src/tests/tests/storage.js @@ -0,0 +1,638 @@ +/* globals badger:false, constants:false */ + +(function () { + +const DOMAIN = "example.com", + SUBDOMAIN = "widgets." + DOMAIN, + SUBSUBDOMAIN = "cdn." + SUBDOMAIN; + +let storage = badger.storage, + actionMap, + snitchMap; + +QUnit.module("Storage", { + before: (assert) => { + // can't initialize globally above + // as they get initialized too early when run by Selenium + actionMap = storage.getStore('action_map'); + snitchMap = storage.getStore('snitch_map'); + + assert.notOk(actionMap.getItem(DOMAIN), + "test domain is not yet in action_map"); + assert.notOk(snitchMap.getItem(DOMAIN), + "test domain is not yet in snitch_map"); + } +}); + +QUnit.test("testGetBadgerStorage", function (assert) { + assert.ok(actionMap.updateObject instanceof Function, "actionMap is a pbstorage"); +}); + +QUnit.test("test BadgerStorage methods", function (assert) { + actionMap.setItem('foo', 'bar'); + assert.equal(actionMap.getItem('foo'), 'bar'); + assert.ok(actionMap.hasItem('foo')); + actionMap.deleteItem('foo'); + assert.notOk(actionMap.hasItem('foo')); +}); + +QUnit.test("test user override of default action for domain", function (assert) { + badger.saveAction("allow", "pbtest.org"); + assert.equal(storage.getAction("pbtest.org"), constants.USER_ALLOW); + badger.saveAction("block", "pbtest.org"); + assert.equal(storage.getAction("pbtest.org"), constants.USER_BLOCK); + badger.saveAction("allow", "pbtest.org"); + assert.equal(storage.getAction("pbtest.org"), constants.USER_ALLOW); + storage.revertUserAction("pbtest.org"); + assert.equal(storage.getAction("pbtest.org"), constants.NO_TRACKING); +}); + +QUnit.test("settings map merging", (assert) => { + let settings_map = storage.getStore('settings_map'); + + // overwrite settings with test values + settings_map.setItem('disabledSites', ['example.com']); + settings_map.setItem('showCounter', true); + + // merge settings + settings_map.merge({ + disabledSites: ['www.nytimes.com'], + showCounter: false, + }); + + // verify + assert.deepEqual( + settings_map.getItem('disabledSites'), + ['example.com', 'www.nytimes.com'], + "disabled site lists are combined when merging settings" + ); + assert.ok(!settings_map.getItem('showCounter'), "other settings are overwritten"); +}); + +// previously: +// https://github.com/EFForg/privacybadger/pull/1911#issuecomment-379896911 +QUnit.test("action map merge copies/breaks references", (assert) => { + let data = { + dnt: false, + heuristicAction: '', + nextUpdateTime: 100, + userAction: 'user_block' + }; + + actionMap.merge({[DOMAIN]: data}); + assert.deepEqual( + actionMap.getItem(DOMAIN), + data, + "test domain was imported"); + + // set a property on the original object + data.userAction = "user_allow"; + + // this should not affect data in storage + assert.equal(actionMap.getItem(DOMAIN).userAction, + "user_block", + "already imported data should be left alone " + + "when modifying object used for import"); +}); + +QUnit.test("action map merge only updates user action", (assert) => { + actionMap.setItem(DOMAIN, + {dnt: false, heuristicAction: '', nextUpdateTime: 100, userAction: ''}); + assert.equal(actionMap.getItem(DOMAIN).nextUpdateTime, 100); + + let newValue = {dnt: true, heuristicAction: constants.BLOCK, + nextUpdateTime: 99, userAction: constants.USER_BLOCK}; + actionMap.merge({[DOMAIN]: newValue}); + assert.equal(actionMap.getItem(DOMAIN).userAction, + constants.USER_BLOCK, + "userAction should be merged if it's set"); + assert.equal(actionMap.getItem(DOMAIN).heuristicAction, '', + 'heuristicAction should never be overwritten'); + + newValue.userAction = ''; + actionMap.merge({[DOMAIN]: newValue}); + assert.equal(actionMap.getItem(DOMAIN).userAction, + constants.USER_BLOCK, + 'blank userAction should not overwrite anything'); +}); + +QUnit.test("action map merge creates new entry if necessary", (assert) => { + assert.notOk(actionMap.hasItem('newsite.com')); + + let newValue = {dnt: false, heuristicAction: constants.BLOCK, + nextUpdateTime: 100, userAction: ''}; + actionMap.merge({'newsite.com': newValue}); + assert.notOk(actionMap.hasItem('newsite.com'), + 'action map entry should not be created for heuristicAction alone'); + + newValue.userAction = constants.USER_BLOCK; + actionMap.merge({'newsite.com': newValue}); + assert.ok(actionMap.hasItem('newsite.com'), + 'action map entry should be created if userAction is set'); + + actionMap.deleteItem('newsite.com'); + + newValue.userAction = ''; + newValue.dnt = true; + actionMap.merge({'newsite.com': newValue}); + assert.ok(actionMap.hasItem('newsite.com'), + 'action map entry should be created if DNT is set'); +}); + +QUnit.test("action map merge updates with latest DNT info", (assert) => { + actionMap.setItem(DOMAIN, + {dnt: false, heuristicAction: '', nextUpdateTime: 100, userAction: ''}); + + // DNT should not be merged if nextUpdateTime is earlier + let newValue = {dnt: true, heuristicAction: '', nextUpdateTime: 99, userAction: ''}; + actionMap.merge({[DOMAIN]: newValue}); + assert.equal(actionMap.getItem(DOMAIN).nextUpdateTime, 100, + 'nextUpdateTime should not be changed to an earlier time'); + assert.notOk(actionMap.getItem(DOMAIN).dnt, + 'DNT value should not be updated by out-of-date information'); + + // DNT should be merged if it's more up-to-date + newValue.nextUpdateTime = 101; + actionMap.merge({[DOMAIN]: newValue}); + assert.equal(actionMap.getItem(DOMAIN).nextUpdateTime, 101, + 'nextUpdateTime should be updated to later time'); + assert.ok(actionMap.getItem(DOMAIN).dnt, + 'DNT value should be updated with more recent information'); +}); + +QUnit.test("action map merge handles missing nextUpdateTime", (assert) => { + let newValue = { + dnt: true, + heuristicAction: '', + userAction: '' + }; + + assert.notOk(newValue.hasOwnProperty('nextUpdateTime'), + "nextUpdateTime is indeed missing from the import"); + + // new DNT domain should be imported + actionMap.merge({[DOMAIN]: newValue}); + assert.deepEqual( + actionMap.getItem(DOMAIN), + Object.assign({ nextUpdateTime: 0 }, newValue), + "test domain was imported and nextUpdateTime got initialized"); + + // existing DNT domain should be left alone + // as we don't know how fresh the import is + newValue.dnt = false; + actionMap.merge({[DOMAIN]: newValue}); + assert.ok(actionMap.getItem(DOMAIN).dnt, + "existing data should be left alone " + + "when unable to determine recency of new data"); + + // now set the timestamp and try again + newValue.nextUpdateTime = 200; + actionMap.merge({[DOMAIN]: newValue}); + assert.notOk(actionMap.getItem(DOMAIN).dnt, + "DNT got overriden now that new data seems fresher"); +}); + +QUnit.test("action map merge handles missing userAction", (assert) => { + let newValue = { + heuristicAction: 'allow', + dnt: true, + nextUpdateTime: 100 + }; + + // import and check that userAction got initialized + actionMap.merge({[DOMAIN]: newValue}); + assert.deepEqual( + actionMap.getItem(DOMAIN), + Object.assign({ userAction: '' }, newValue), + "test domain was imported and userAction got initialized"); +}); + +QUnit.test("action map merge handles missing dnt", (assert) => { + let newValue = { + heuristicAction: 'block', + userAction: 'user_allow' + }; + + // import and check that userAction got initialized + actionMap.merge({[DOMAIN]: newValue}); + assert.deepEqual( + actionMap.getItem(DOMAIN), + Object.assign({ dnt: false, nextUpdateTime: 0 }, newValue), + "test domain was imported and DNT got initialized"); +}); + +QUnit.test("action map merge handles subdomains correctly", (assert) => { + actionMap.setItem('testsite.com', + {dnt: false, heuristicAction: '', nextUpdateTime: 100, userAction: ''}); + + let newValue = {dnt: true, heuristicAction: '', nextUpdateTime: 100, userAction: ''}; + + actionMap.merge({'s1.testsite.com': newValue}); + assert.ok(actionMap.hasItem('s1.testsite.com'), + 'Subdomains should be merged if they honor DNT'); + + newValue.dnt = false; + actionMap.merge({'s2.testsite.com': newValue}); + assert.notOk(actionMap.hasItem('s2.testsite.com'), + "Subdomains should not be merged if they don't honor DNT"); +}); + +QUnit.test("snitch map merging", (assert) => { + snitchMap.merge({[DOMAIN]: ['firstparty.org']}); + assert.ok(snitchMap.getItem(DOMAIN).indexOf('firstparty.org') > -1); + + // Check to make sure existing and new domain are present + snitchMap.merge({[DOMAIN]: ['firstparty2.org']}); + assert.ok(snitchMap.getItem(DOMAIN).indexOf('firstparty.org') > -1); + assert.ok(snitchMap.getItem(DOMAIN).indexOf('firstparty2.org') > -1); + + // Verify 'block' status is triggered once TRACKING_THRESHOLD is hit + assert.equal(actionMap.getItem(DOMAIN).heuristicAction, "allow"); + snitchMap.merge({[DOMAIN]: ["firstparty3.org"]}); + assert.equal(actionMap.getItem(DOMAIN).heuristicAction, "block"); +}); + +QUnit.test("blocking cascades", (assert) => { + // mark domain for blocking + storage.setupHeuristicAction(DOMAIN, constants.BLOCK); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.BLOCK, + "domain is marked for blocking directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.BLOCK, + "domain is marked for blocking" + ); + + // check that subdomain inherits blocking + assert.equal( + storage.getAction(SUBDOMAIN), + constants.NO_TRACKING, + "subdomain is not marked for blocking directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.BLOCK, + "subdomain is marked for blocking (via parent domain)" + ); + + // check that subsubdomain inherits blocking + assert.equal( + storage.getAction(SUBSUBDOMAIN), + constants.NO_TRACKING, + "subsubdomain is not marked for blocking directly" + ); + assert.equal( + storage.getBestAction(SUBSUBDOMAIN), + constants.BLOCK, + "subsubdomain is marked for blocking (via grandparent domain)" + ); +}); + +QUnit.test("DNT does not cascade", (assert) => { + storage.setupDNT(DOMAIN); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.DNT, + "domain is marked as DNT directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.DNT, + "domain is marked as DNT" + ); + + // check that subdomain does not inherit DNT + assert.equal( + storage.getAction(SUBDOMAIN), + constants.NO_TRACKING, + "subdomain is not marked as DNT directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.NO_TRACKING, + "subdomain is not marked as DNT (via parent domain)" + ); +}); + +QUnit.test("DNT does not return as an action if user has chosen not to", (assert) => { + let settings_map = storage.getStore('settings_map'); + settings_map.setItem("checkForDNTPolicy", false); + storage.setupDNT(DOMAIN); + + assert.equal( + storage.getAction(DOMAIN), + constants.NO_TRACKING, + "domain is marked as DNT directly, but returns as NO_TRACKING because user has disabled DNT" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.NO_TRACKING, + "domain is marked as DNT, but returns as NO_TRACKING because user has disabled DNT" + ); +}); + +QUnit.test("blocking still cascades after domain declares DNT", (assert) => { + storage.setupHeuristicAction(DOMAIN, constants.BLOCK); + storage.setupDNT(DOMAIN); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN, true), + constants.BLOCK, + "domain is marked for blocking directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.DNT, + "domain is marked as DNT" + ); + + // check that subdomain inherits blocking + assert.equal( + storage.getAction(SUBDOMAIN), + constants.NO_TRACKING, + "subdomain is not marked for blocking directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.BLOCK, + "subdomain is marked for blocking (via parent domain)" + ); +}); + +QUnit.test("cascading doesn't work the other way", (assert) => { + // mark subdomain for blocking + storage.setupHeuristicAction(SUBDOMAIN, constants.BLOCK); + + // check subdomain itself + assert.equal( + storage.getAction(SUBDOMAIN), + constants.BLOCK, + "subdomain is marked for blocking directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.BLOCK, + "subdomain is marked for blocking" + ); + + // check that parent domain does not inherit blocking + assert.equal( + storage.getAction(DOMAIN), + constants.NO_TRACKING, + "domain is not marked for blocking directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.NO_TRACKING, + "domain is not marked for blocking" + ); +}); + +QUnit.test("blocking overrules allowing", (assert) => { + // mark domain for blocking + storage.setupHeuristicAction(DOMAIN, constants.BLOCK); + // mark subsubdomain as "allow" (not-yet-over-the-threshold tracker) + storage.setupHeuristicAction(SUBSUBDOMAIN, constants.ALLOW); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.BLOCK, + "domain is marked for blocking directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.BLOCK, + "domain is marked for blocking" + ); + + // check that subsubdomain inherits blocking + assert.equal( + storage.getAction(SUBSUBDOMAIN), + constants.ALLOW, + "subdomain is marked as 'allow' directly" + ); + assert.equal( + storage.getBestAction(SUBSUBDOMAIN), + constants.BLOCK, + "subsubdomain is marked for blocking (via grandparent domain)" + ); +}); + +QUnit.test("cookieblocking overrules blocking", (assert) => { + // mark domain for cookieblocking + storage.setupHeuristicAction(DOMAIN, constants.COOKIEBLOCK); + // mark subdomain for blocking + storage.setupHeuristicAction(SUBDOMAIN, constants.BLOCK); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.COOKIEBLOCK, + "domain is marked for cookieblocking directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.COOKIEBLOCK, + "domain is marked for cookieblocking" + ); + + // check that subdomain inherits cookieblocking + assert.equal( + storage.getAction(SUBDOMAIN), + constants.BLOCK, + "subdomain is marked for blocking directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.COOKIEBLOCK, + "subdomain is marked for cookieblocking (via parent domain)" + ); +}); + +QUnit.test("user actions overrule everything else", (assert) => { + storage.setupUserAction(DOMAIN, constants.USER_BLOCK); + storage.setupHeuristicAction(SUBDOMAIN, constants.COOKIEBLOCK); + storage.setupDNT(SUBSUBDOMAIN); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.USER_BLOCK, + "domain is marked as userblock directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.USER_BLOCK, + "domain is marked as userblock" + ); + + // check subdomain + assert.equal( + storage.getAction(SUBDOMAIN), + constants.COOKIEBLOCK, + "subdomain is marked for cookie blocking directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.USER_BLOCK, + "subdomain is marked as userblock" + ); + + // check subsubdomain + assert.equal( + storage.getAction(SUBSUBDOMAIN), + constants.DNT, + "subsubdomain is marked as DNT directly" + ); + assert.equal( + storage.getBestAction(SUBSUBDOMAIN), + constants.USER_BLOCK, + "subsubdomain is marked as userblock" + ); +}); + +// all three user actions are equally important +// but the one closest to the FQDN being checked should win +QUnit.test("specificity of rules of equal priority", (assert) => { + storage.setupUserAction(DOMAIN, constants.USER_BLOCK); + storage.setupUserAction(SUBDOMAIN, constants.USER_ALLOW); + storage.setupUserAction(SUBSUBDOMAIN, constants.USER_COOKIEBLOCK); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + constants.USER_BLOCK, + "domain is marked as userblock directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.USER_BLOCK, + "domain is marked as userblock" + ); + + // check subdomain + assert.equal( + storage.getAction(SUBDOMAIN), + constants.USER_ALLOW, + "subdomain is marked as userallow directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.USER_ALLOW, + "subdomain is marked as userallow" + ); + + // check subsubdomain + assert.equal( + storage.getAction(SUBSUBDOMAIN), + constants.USER_COOKIEBLOCK, + "subsubdomain is marked as usercookieblock directly" + ); + assert.equal( + storage.getBestAction(SUBSUBDOMAIN), + constants.USER_COOKIEBLOCK, + "subsubdomain is marked as usercookieblock" + ); +}); + +QUnit.test("unexpected heuristic actions are ignored", (assert) => { + storage.setupHeuristicAction(DOMAIN, "foo"); + storage.setupHeuristicAction(SUBDOMAIN, constants.ALLOW); + storage.setupHeuristicAction(SUBSUBDOMAIN, "bar"); + + // check domain itself + assert.equal( + storage.getAction(DOMAIN), + "foo", + "domain is marked as 'foo' directly" + ); + assert.equal( + storage.getBestAction(DOMAIN), + constants.NO_TRACKING, + "best action for domain is 'no tracking'" + ); + + // check subdomain + assert.equal( + storage.getAction(SUBDOMAIN), + constants.ALLOW, + "subdomain is marked as 'allow' directly" + ); + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.ALLOW, + "best action for subdomain is 'allow'" + ); + + // check subsubdomain + assert.equal( + storage.getAction(SUBSUBDOMAIN), + "bar", + "subsubdomain is marked as 'bar' directly" + ); + assert.equal( + storage.getBestAction(SUBSUBDOMAIN), + constants.ALLOW, + "best action for subsubdomain is 'allow'" + ); +}); + +function checkCookieblocking(assert) { + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.NO_TRACKING, + "subdomain is not yet (cookie)blocked" + ); + assert.ok( + storage.wouldGetCookieblocked(SUBDOMAIN), + "subdomain would get cookieblocked if blocked" + ); + + // block the subdomain + badger.heuristicBlocking.blocklistOrigin(DOMAIN, SUBDOMAIN); + + assert.equal( + storage.getBestAction(SUBDOMAIN), + constants.COOKIEBLOCK, + "subdomain is cookieblocked" + ); + assert.ok( + storage.wouldGetCookieblocked(SUBDOMAIN), + "subdomain would get/is cookieblocked" + ); +} + +QUnit.test("checking cookieblock potential for yellowlisted subdomain", (assert) => { + assert.notOk( + storage.wouldGetCookieblocked(SUBDOMAIN), + "subdomain wouldn't get cookieblocked if blocked" + ); + + // add subdomain to yellowlist + storage.getStore('cookieblock_list').setItem(SUBDOMAIN, true); + + checkCookieblocking(assert); +}); + +QUnit.test("checking cookieblock potential for subdomain with yellowlisted base domain", (assert) => { + assert.notOk( + storage.wouldGetCookieblocked(SUBDOMAIN), + "subdomain wouldn't get cookieblocked if blocked" + ); + + // add base domain to yellowlist + storage.getStore('cookieblock_list').setItem(DOMAIN, true); + + checkCookieblocking(assert); +}); + +}()); |