diff options
Diffstat (limited to 'toolkit/components/url-classifier/tests/unit/head_urlclassifier.js')
-rw-r--r-- | toolkit/components/url-classifier/tests/unit/head_urlclassifier.js | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js new file mode 100644 index 000000000..21849ced7 --- /dev/null +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -0,0 +1,429 @@ +//* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- * +function dumpn(s) { + dump(s + "\n"); +} + +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; +const NS_APP_USER_PROFILE_LOCAL_50_DIR = "ProfLD"; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; + +Cu.import("resource://testing-common/httpd.js"); + +do_get_profile(); + +var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + +var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + +var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + +// Disable hashcompleter noise for tests +var prefBranch = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); +prefBranch.setIntPref("urlclassifier.gethashnoise", 0); + +// Enable malware/phishing checking for tests +prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true); +prefBranch.setBoolPref("browser.safebrowsing.blockedURIs.enabled", true); +prefBranch.setBoolPref("browser.safebrowsing.phishing.enabled", true); + +// Enable all completions for tests +prefBranch.setCharPref("urlclassifier.disallow_completions", ""); + +// Hash completion timeout +prefBranch.setIntPref("urlclassifier.gethash.timeout_ms", 5000); + +function delFile(name) { + try { + // Delete a previously created sqlite file + var file = dirSvc.get('ProfLD', Ci.nsIFile); + file.append(name); + if (file.exists()) + file.remove(false); + } catch(e) { + } +} + +function cleanUp() { + delFile("urlclassifier3.sqlite"); + delFile("safebrowsing/classifier.hashkey"); + delFile("safebrowsing/test-phish-simple.sbstore"); + delFile("safebrowsing/test-malware-simple.sbstore"); + delFile("safebrowsing/test-unwanted-simple.sbstore"); + delFile("safebrowsing/test-block-simple.sbstore"); + delFile("safebrowsing/test-track-simple.sbstore"); + delFile("safebrowsing/test-trackwhite-simple.sbstore"); + delFile("safebrowsing/test-phish-simple.pset"); + delFile("safebrowsing/test-malware-simple.pset"); + delFile("safebrowsing/test-unwanted-simple.pset"); + delFile("safebrowsing/test-block-simple.pset"); + delFile("safebrowsing/test-track-simple.pset"); + delFile("safebrowsing/test-trackwhite-simple.pset"); + delFile("safebrowsing/moz-phish-simple.sbstore"); + delFile("safebrowsing/moz-phish-simple.pset"); + delFile("testLarge.pset"); + delFile("testNoDelta.pset"); +} + +// Update uses allTables by default +var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple"; +var mozTables = "moz-phish-simple"; + +var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService); +var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"] + .getService(Ci.nsIUrlClassifierStreamUpdater); + + +/* + * Builds an update from an object that looks like: + *{ "test-phish-simple" : [{ + * "chunkType" : "a", // 'a' is assumed if not specified + * "chunkNum" : 1, // numerically-increasing chunk numbers are assumed + * // if not specified + * "urls" : [ "foo.com/a", "foo.com/b", "bar.com/" ] + * } + */ + +function buildUpdate(update, hashSize) { + if (!hashSize) { + hashSize = 32; + } + var updateStr = "n:1000\n"; + + for (var tableName in update) { + if (tableName != "") + updateStr += "i:" + tableName + "\n"; + var chunks = update[tableName]; + for (var j = 0; j < chunks.length; j++) { + var chunk = chunks[j]; + var chunkType = chunk.chunkType ? chunk.chunkType : 'a'; + var chunkNum = chunk.chunkNum ? chunk.chunkNum : j; + updateStr += chunkType + ':' + chunkNum + ':' + hashSize; + + if (chunk.urls) { + var chunkData = chunk.urls.join("\n"); + updateStr += ":" + chunkData.length + "\n" + chunkData; + } + + updateStr += "\n"; + } + } + + return updateStr; +} + +function buildPhishingUpdate(chunks, hashSize) { + return buildUpdate({"test-phish-simple" : chunks}, hashSize); +} + +function buildMalwareUpdate(chunks, hashSize) { + return buildUpdate({"test-malware-simple" : chunks}, hashSize); +} + +function buildUnwantedUpdate(chunks, hashSize) { + return buildUpdate({"test-unwanted-simple" : chunks}, hashSize); +} + +function buildBlockedUpdate(chunks, hashSize) { + return buildUpdate({"test-block-simple" : chunks}, hashSize); +} + +function buildMozPhishingUpdate(chunks, hashSize) { + return buildUpdate({"moz-phish-simple" : chunks}, hashSize); +} + +function buildBareUpdate(chunks, hashSize) { + return buildUpdate({"" : chunks}, hashSize); +} + +/** + * Performs an update of the dbservice manually, bypassing the stream updater + */ +function doSimpleUpdate(updateText, success, failure) { + var listener = { + QueryInterface: function(iid) + { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + updateUrlRequested: function(url) { }, + streamFinished: function(status) { }, + updateError: function(errorCode) { failure(errorCode); }, + updateSuccess: function(requestedTimeout) { success(requestedTimeout); } + }; + + dbservice.beginUpdate(listener, allTables); + dbservice.beginStream("", ""); + dbservice.updateStream(updateText); + dbservice.finishStream(); + dbservice.finishUpdate(); +} + +/** + * Simulates a failed database update. + */ +function doErrorUpdate(tables, success, failure) { + var listener = { + QueryInterface: function(iid) + { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + updateUrlRequested: function(url) { }, + streamFinished: function(status) { }, + updateError: function(errorCode) { success(errorCode); }, + updateSuccess: function(requestedTimeout) { failure(requestedTimeout); } + }; + + dbservice.beginUpdate(listener, tables, null); + dbservice.beginStream("", ""); + dbservice.cancelUpdate(); +} + +/** + * Performs an update of the dbservice using the stream updater and a + * data: uri + */ +function doStreamUpdate(updateText, success, failure, downloadFailure) { + var dataUpdate = "data:," + encodeURIComponent(updateText); + + if (!downloadFailure) { + downloadFailure = failure; + } + + streamUpdater.downloadUpdates(allTables, "", true, + dataUpdate, success, failure, downloadFailure); +} + +var gAssertions = { + +tableData : function(expectedTables, cb) +{ + dbservice.getTables(function(tables) { + // rebuild the tables in a predictable order. + var parts = tables.split("\n"); + while (parts[parts.length - 1] == '') { + parts.pop(); + } + parts.sort(); + tables = parts.join("\n"); + + do_check_eq(tables, expectedTables); + cb(); + }); +}, + +checkUrls: function(urls, expected, cb, useMoz = false) +{ + // work with a copy of the list. + urls = urls.slice(0); + var doLookup = function() { + if (urls.length > 0) { + var tables = useMoz ? mozTables : allTables; + var fragment = urls.shift(); + var principal = secMan.createCodebasePrincipal(iosvc.newURI("http://" + fragment, null, null), {}); + dbservice.lookup(principal, tables, + function(arg) { + do_check_eq(expected, arg); + doLookup(); + }, true); + } else { + cb(); + } + }; + doLookup(); +}, + +checkTables: function(url, expected, cb) +{ + var principal = secMan.createCodebasePrincipal(iosvc.newURI("http://" + url, null, null), {}); + dbservice.lookup(principal, allTables, function(tables) { + // Rebuild tables in a predictable order. + var parts = tables.split(","); + while (parts[parts.length - 1] == '') { + parts.pop(); + } + parts.sort(); + tables = parts.join(","); + do_check_eq(tables, expected); + cb(); + }, true); +}, + +urlsDontExist: function(urls, cb) +{ + this.checkUrls(urls, '', cb); +}, + +urlsExist: function(urls, cb) +{ + this.checkUrls(urls, 'test-phish-simple', cb); +}, + +malwareUrlsExist: function(urls, cb) +{ + this.checkUrls(urls, 'test-malware-simple', cb); +}, + +unwantedUrlsExist: function(urls, cb) +{ + this.checkUrls(urls, 'test-unwanted-simple', cb); +}, + +blockedUrlsExist: function(urls, cb) +{ + this.checkUrls(urls, 'test-block-simple', cb); +}, + +mozPhishingUrlsExist: function(urls, cb) +{ + this.checkUrls(urls, 'moz-phish-simple', cb, true); +}, + +subsDontExist: function(urls, cb) +{ + // XXX: there's no interface for checking items in the subs table + cb(); +}, + +subsExist: function(urls, cb) +{ + // XXX: there's no interface for checking items in the subs table + cb(); +}, + +urlExistInMultipleTables: function(data, cb) +{ + this.checkTables(data["url"], data["tables"], cb); +} + +}; + +/** + * Check a set of assertions against the gAssertions table. + */ +function checkAssertions(assertions, doneCallback) +{ + var checkAssertion = function() { + for (var i in assertions) { + var data = assertions[i]; + delete assertions[i]; + gAssertions[i](data, checkAssertion); + return; + } + + doneCallback(); + } + + checkAssertion(); +} + +function updateError(arg) +{ + do_throw(arg); +} + +// Runs a set of updates, and then checks a set of assertions. +function doUpdateTest(updates, assertions, successCallback, errorCallback) { + var errorUpdate = function() { + checkAssertions(assertions, errorCallback); + } + + var runUpdate = function() { + if (updates.length > 0) { + var update = updates.shift(); + doStreamUpdate(update, runUpdate, errorUpdate, null); + } else { + checkAssertions(assertions, successCallback); + } + } + + runUpdate(); +} + +var gTests; +var gNextTest = 0; + +function runNextTest() +{ + if (gNextTest >= gTests.length) { + do_test_finished(); + return; + } + + dbservice.resetDatabase(); + dbservice.setHashCompleter('test-phish-simple', null); + + let test = gTests[gNextTest++]; + dump("running " + test.name + "\n"); + test(); +} + +function runTests(tests) +{ + gTests = tests; + runNextTest(); +} + +var timerArray = []; + +function Timer(delay, cb) { + this.cb = cb; + var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(this, delay, timer.TYPE_ONE_SHOT); + timerArray.push(timer); +} + +Timer.prototype = { +QueryInterface: function(iid) { + if (!iid.equals(Ci.nsISupports) && !iid.equals(Ci.nsITimerCallback)) { + throw Cr.NS_ERROR_NO_INTERFACE; + } + return this; + }, +notify: function(timer) { + this.cb(); + } +} + +// LFSRgenerator is a 32-bit linear feedback shift register random number +// generator. It is highly predictable and is not intended to be used for +// cryptography but rather to allow easier debugging than a test that uses +// Math.random(). +function LFSRgenerator(seed) { + // Force |seed| to be a number. + seed = +seed; + // LFSR generators do not work with a value of 0. + if (seed == 0) + seed = 1; + + this._value = seed; +} +LFSRgenerator.prototype = { + // nextNum returns a random unsigned integer of in the range [0,2^|bits|]. + nextNum: function(bits) { + if (!bits) + bits = 32; + + let val = this._value; + // Taps are 32, 22, 2 and 1. + let bit = ((val >>> 0) ^ (val >>> 10) ^ (val >>> 30) ^ (val >>> 31)) & 1; + val = (val >>> 1) | (bit << 31); + this._value = val; + + return (val >>> (32 - bits)); + }, +}; + +cleanUp(); |