1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,365 @@ 1.4 +//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- * 1.5 +function dumpn(s) { 1.6 + dump(s + "\n"); 1.7 +} 1.8 + 1.9 +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; 1.10 +const NS_APP_USER_PROFILE_LOCAL_50_DIR = "ProfLD"; 1.11 + 1.12 +const Cc = Components.classes; 1.13 +const Ci = Components.interfaces; 1.14 +const Cu = Components.utils; 1.15 +const Cr = Components.results; 1.16 + 1.17 +Cu.import("resource://testing-common/httpd.js"); 1.18 + 1.19 +do_get_profile(); 1.20 + 1.21 +var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); 1.22 + 1.23 +var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 1.24 + 1.25 +var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] 1.26 + .getService(Ci.nsIScriptSecurityManager); 1.27 + 1.28 +// Disable hashcompleter noise for tests 1.29 +var prefBranch = Cc["@mozilla.org/preferences-service;1"]. 1.30 + getService(Ci.nsIPrefBranch); 1.31 +prefBranch.setIntPref("urlclassifier.gethashnoise", 0); 1.32 + 1.33 +// Enable malware/phishing checking for tests 1.34 +prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true); 1.35 +prefBranch.setBoolPref("browser.safebrowsing.enabled", true); 1.36 + 1.37 +// Enable all completions for tests 1.38 +prefBranch.setCharPref("urlclassifier.disallow_completions", ""); 1.39 + 1.40 +function delFile(name) { 1.41 + try { 1.42 + // Delete a previously created sqlite file 1.43 + var file = dirSvc.get('ProfLD', Ci.nsIFile); 1.44 + file.append(name); 1.45 + if (file.exists()) 1.46 + file.remove(false); 1.47 + } catch(e) { 1.48 + } 1.49 +} 1.50 + 1.51 +function cleanUp() { 1.52 + delFile("urlclassifier3.sqlite"); 1.53 + delFile("safebrowsing/classifier.hashkey"); 1.54 + delFile("safebrowsing/test-phish-simple.sbstore"); 1.55 + delFile("safebrowsing/test-malware-simple.sbstore"); 1.56 + delFile("safebrowsing/test-phish-simple.cache"); 1.57 + delFile("safebrowsing/test-malware-simple.cache"); 1.58 + delFile("safebrowsing/test-phish-simple.pset"); 1.59 + delFile("safebrowsing/test-malware-simple.pset"); 1.60 +} 1.61 + 1.62 +var allTables = "test-phish-simple,test-malware-simple"; 1.63 + 1.64 +var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService); 1.65 +var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"] 1.66 + .getService(Ci.nsIUrlClassifierStreamUpdater); 1.67 + 1.68 + 1.69 +/* 1.70 + * Builds an update from an object that looks like: 1.71 + *{ "test-phish-simple" : [{ 1.72 + * "chunkType" : "a", // 'a' is assumed if not specified 1.73 + * "chunkNum" : 1, // numerically-increasing chunk numbers are assumed 1.74 + * // if not specified 1.75 + * "urls" : [ "foo.com/a", "foo.com/b", "bar.com/" ] 1.76 + * } 1.77 + */ 1.78 + 1.79 +function buildUpdate(update, hashSize) { 1.80 + if (!hashSize) { 1.81 + hashSize = 32; 1.82 + } 1.83 + var updateStr = "n:1000\n"; 1.84 + 1.85 + for (var tableName in update) { 1.86 + if (tableName != "") 1.87 + updateStr += "i:" + tableName + "\n"; 1.88 + var chunks = update[tableName]; 1.89 + for (var j = 0; j < chunks.length; j++) { 1.90 + var chunk = chunks[j]; 1.91 + var chunkType = chunk.chunkType ? chunk.chunkType : 'a'; 1.92 + var chunkNum = chunk.chunkNum ? chunk.chunkNum : j; 1.93 + updateStr += chunkType + ':' + chunkNum + ':' + hashSize; 1.94 + 1.95 + if (chunk.urls) { 1.96 + var chunkData = chunk.urls.join("\n"); 1.97 + updateStr += ":" + chunkData.length + "\n" + chunkData; 1.98 + } 1.99 + 1.100 + updateStr += "\n"; 1.101 + } 1.102 + } 1.103 + 1.104 + return updateStr; 1.105 +} 1.106 + 1.107 +function buildPhishingUpdate(chunks, hashSize) { 1.108 + return buildUpdate({"test-phish-simple" : chunks}, hashSize); 1.109 +} 1.110 + 1.111 +function buildMalwareUpdate(chunks, hashSize) { 1.112 + return buildUpdate({"test-malware-simple" : chunks}, hashSize); 1.113 +} 1.114 + 1.115 +function buildBareUpdate(chunks, hashSize) { 1.116 + return buildUpdate({"" : chunks}, hashSize); 1.117 +} 1.118 + 1.119 +/** 1.120 + * Performs an update of the dbservice manually, bypassing the stream updater 1.121 + */ 1.122 +function doSimpleUpdate(updateText, success, failure) { 1.123 + var listener = { 1.124 + QueryInterface: function(iid) 1.125 + { 1.126 + if (iid.equals(Ci.nsISupports) || 1.127 + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) 1.128 + return this; 1.129 + throw Cr.NS_ERROR_NO_INTERFACE; 1.130 + }, 1.131 + 1.132 + updateUrlRequested: function(url) { }, 1.133 + streamFinished: function(status) { }, 1.134 + updateError: function(errorCode) { failure(errorCode); }, 1.135 + updateSuccess: function(requestedTimeout) { success(requestedTimeout); } 1.136 + }; 1.137 + 1.138 + dbservice.beginUpdate(listener, 1.139 + "test-phish-simple,test-malware-simple"); 1.140 + dbservice.beginStream("", ""); 1.141 + dbservice.updateStream(updateText); 1.142 + dbservice.finishStream(); 1.143 + dbservice.finishUpdate(); 1.144 +} 1.145 + 1.146 +/** 1.147 + * Simulates a failed database update. 1.148 + */ 1.149 +function doErrorUpdate(tables, success, failure) { 1.150 + var listener = { 1.151 + QueryInterface: function(iid) 1.152 + { 1.153 + if (iid.equals(Ci.nsISupports) || 1.154 + iid.equals(Ci.nsIUrlClassifierUpdateObserver)) 1.155 + return this; 1.156 + throw Cr.NS_ERROR_NO_INTERFACE; 1.157 + }, 1.158 + 1.159 + updateUrlRequested: function(url) { }, 1.160 + streamFinished: function(status) { }, 1.161 + updateError: function(errorCode) { success(errorCode); }, 1.162 + updateSuccess: function(requestedTimeout) { failure(requestedTimeout); } 1.163 + }; 1.164 + 1.165 + dbservice.beginUpdate(listener, tables, null); 1.166 + dbservice.beginStream("", ""); 1.167 + dbservice.cancelUpdate(); 1.168 +} 1.169 + 1.170 +/** 1.171 + * Performs an update of the dbservice using the stream updater and a 1.172 + * data: uri 1.173 + */ 1.174 +function doStreamUpdate(updateText, success, failure, downloadFailure) { 1.175 + var dataUpdate = "data:," + encodeURIComponent(updateText); 1.176 + 1.177 + if (!downloadFailure) 1.178 + downloadFailure = failure; 1.179 + 1.180 + streamUpdater.updateUrl = dataUpdate; 1.181 + streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple", "", 1.182 + success, failure, downloadFailure); 1.183 +} 1.184 + 1.185 +var gAssertions = { 1.186 + 1.187 +tableData : function(expectedTables, cb) 1.188 +{ 1.189 + dbservice.getTables(function(tables) { 1.190 + // rebuild the tables in a predictable order. 1.191 + var parts = tables.split("\n"); 1.192 + while (parts[parts.length - 1] == '') { 1.193 + parts.pop(); 1.194 + } 1.195 + parts.sort(); 1.196 + tables = parts.join("\n"); 1.197 + 1.198 + do_check_eq(tables, expectedTables); 1.199 + cb(); 1.200 + }); 1.201 +}, 1.202 + 1.203 +checkUrls: function(urls, expected, cb) 1.204 +{ 1.205 + // work with a copy of the list. 1.206 + urls = urls.slice(0); 1.207 + var doLookup = function() { 1.208 + if (urls.length > 0) { 1.209 + var fragment = urls.shift(); 1.210 + var principal = secMan.getNoAppCodebasePrincipal(iosvc.newURI("http://" + fragment, null, null)); 1.211 + dbservice.lookup(principal, allTables, 1.212 + function(arg) { 1.213 + do_check_eq(expected, arg); 1.214 + doLookup(); 1.215 + }, true); 1.216 + } else { 1.217 + cb(); 1.218 + } 1.219 + }; 1.220 + doLookup(); 1.221 +}, 1.222 + 1.223 +urlsDontExist: function(urls, cb) 1.224 +{ 1.225 + this.checkUrls(urls, '', cb); 1.226 +}, 1.227 + 1.228 +urlsExist: function(urls, cb) 1.229 +{ 1.230 + this.checkUrls(urls, 'test-phish-simple', cb); 1.231 +}, 1.232 + 1.233 +malwareUrlsExist: function(urls, cb) 1.234 +{ 1.235 + this.checkUrls(urls, 'test-malware-simple', cb); 1.236 +}, 1.237 + 1.238 +subsDontExist: function(urls, cb) 1.239 +{ 1.240 + // XXX: there's no interface for checking items in the subs table 1.241 + cb(); 1.242 +}, 1.243 + 1.244 +subsExist: function(urls, cb) 1.245 +{ 1.246 + // XXX: there's no interface for checking items in the subs table 1.247 + cb(); 1.248 +} 1.249 + 1.250 +}; 1.251 + 1.252 +/** 1.253 + * Check a set of assertions against the gAssertions table. 1.254 + */ 1.255 +function checkAssertions(assertions, doneCallback) 1.256 +{ 1.257 + var checkAssertion = function() { 1.258 + for (var i in assertions) { 1.259 + var data = assertions[i]; 1.260 + delete assertions[i]; 1.261 + gAssertions[i](data, checkAssertion); 1.262 + return; 1.263 + } 1.264 + 1.265 + doneCallback(); 1.266 + } 1.267 + 1.268 + checkAssertion(); 1.269 +} 1.270 + 1.271 +function updateError(arg) 1.272 +{ 1.273 + do_throw(arg); 1.274 +} 1.275 + 1.276 +// Runs a set of updates, and then checks a set of assertions. 1.277 +function doUpdateTest(updates, assertions, successCallback, errorCallback) { 1.278 + var errorUpdate = function() { 1.279 + checkAssertions(assertions, errorCallback); 1.280 + } 1.281 + 1.282 + var runUpdate = function() { 1.283 + if (updates.length > 0) { 1.284 + var update = updates.shift(); 1.285 + doStreamUpdate(update, runUpdate, errorUpdate, null); 1.286 + } else { 1.287 + checkAssertions(assertions, successCallback); 1.288 + } 1.289 + } 1.290 + 1.291 + runUpdate(); 1.292 +} 1.293 + 1.294 +var gTests; 1.295 +var gNextTest = 0; 1.296 + 1.297 +function runNextTest() 1.298 +{ 1.299 + if (gNextTest >= gTests.length) { 1.300 + do_test_finished(); 1.301 + return; 1.302 + } 1.303 + 1.304 + dbservice.resetDatabase(); 1.305 + dbservice.setHashCompleter('test-phish-simple', null); 1.306 + 1.307 + let test = gTests[gNextTest++]; 1.308 + dump("running " + test.name + "\n"); 1.309 + test(); 1.310 +} 1.311 + 1.312 +function runTests(tests) 1.313 +{ 1.314 + gTests = tests; 1.315 + runNextTest(); 1.316 +} 1.317 + 1.318 +var timerArray = []; 1.319 + 1.320 +function Timer(delay, cb) { 1.321 + this.cb = cb; 1.322 + var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.323 + timer.initWithCallback(this, delay, timer.TYPE_ONE_SHOT); 1.324 + timerArray.push(timer); 1.325 +} 1.326 + 1.327 +Timer.prototype = { 1.328 +QueryInterface: function(iid) { 1.329 + if (!iid.equals(Ci.nsISupports) && !iid.equals(Ci.nsITimerCallback)) { 1.330 + throw Cr.NS_ERROR_NO_INTERFACE; 1.331 + } 1.332 + return this; 1.333 + }, 1.334 +notify: function(timer) { 1.335 + this.cb(); 1.336 + } 1.337 +} 1.338 + 1.339 +// LFSRgenerator is a 32-bit linear feedback shift register random number 1.340 +// generator. It is highly predictable and is not intended to be used for 1.341 +// cryptography but rather to allow easier debugging than a test that uses 1.342 +// Math.random(). 1.343 +function LFSRgenerator(seed) { 1.344 + // Force |seed| to be a number. 1.345 + seed = +seed; 1.346 + // LFSR generators do not work with a value of 0. 1.347 + if (seed == 0) 1.348 + seed = 1; 1.349 + 1.350 + this._value = seed; 1.351 +} 1.352 +LFSRgenerator.prototype = { 1.353 + // nextNum returns a random unsigned integer of in the range [0,2^|bits|]. 1.354 + nextNum: function(bits) { 1.355 + if (!bits) 1.356 + bits = 32; 1.357 + 1.358 + let val = this._value; 1.359 + // Taps are 32, 22, 2 and 1. 1.360 + let bit = ((val >>> 0) ^ (val >>> 10) ^ (val >>> 30) ^ (val >>> 31)) & 1; 1.361 + val = (val >>> 1) | (bit << 31); 1.362 + this._value = val; 1.363 + 1.364 + return (val >>> (32 - bits)); 1.365 + }, 1.366 +}; 1.367 + 1.368 +cleanUp();