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