1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/downloads/test/unit/test_app_rep.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,290 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +Cu.import('resource://gre/modules/NetUtil.jsm'); 1.11 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.12 + 1.13 +const gAppRep = Cc["@mozilla.org/downloads/application-reputation-service;1"]. 1.14 + getService(Ci.nsIApplicationReputationService); 1.15 +let gHttpServ = null; 1.16 +let gTables = {}; 1.17 + 1.18 +let ALLOW_LIST = 0; 1.19 +let BLOCK_LIST = 1; 1.20 +let NO_LIST = 2; 1.21 + 1.22 +function readFileToString(aFilename) { 1.23 + let f = do_get_file(aFilename); 1.24 + let stream = Cc["@mozilla.org/network/file-input-stream;1"] 1.25 + .createInstance(Ci.nsIFileInputStream); 1.26 + stream.init(f, -1, 0, 0); 1.27 + let buf = NetUtil.readInputStreamToString(stream, stream.available()); 1.28 + return buf; 1.29 +} 1.30 + 1.31 +// Registers a table for which to serve update chunks. Returns a promise that 1.32 +// resolves when that chunk has been downloaded. 1.33 +function registerTableUpdate(aTable, aFilename) { 1.34 + // If we haven't been given an update for this table yet, add it to the map 1.35 + if (!(aTable in gTables)) { 1.36 + gTables[aTable] = []; 1.37 + } 1.38 + 1.39 + // The number of chunks associated with this table. 1.40 + let numChunks = gTables[aTable].length + 1; 1.41 + let redirectPath = "/" + aTable + "-" + numChunks; 1.42 + let redirectUrl = "localhost:4444" + redirectPath; 1.43 + 1.44 + // Store redirect url for that table so we can return it later when we 1.45 + // process an update request. 1.46 + gTables[aTable].push(redirectUrl); 1.47 + 1.48 + gHttpServ.registerPathHandler(redirectPath, function(request, response) { 1.49 + do_print("Mock safebrowsing server handling request for " + redirectPath); 1.50 + let contents = readFileToString(aFilename); 1.51 + do_print("Length of " + aFilename + ": " + contents.length); 1.52 + response.setHeader("Content-Type", 1.53 + "application/vnd.google.safebrowsing-update", false); 1.54 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.55 + response.bodyOutputStream.write(contents, contents.length); 1.56 + }); 1.57 +} 1.58 + 1.59 +function run_test() { 1.60 + // Set up a local HTTP server to return bad verdicts. 1.61 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", 1.62 + "http://localhost:4444/download"); 1.63 + // Ensure safebrowsing is enabled for this test, even if the app 1.64 + // doesn't have it enabled. 1.65 + Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true); 1.66 + do_register_cleanup(function() { 1.67 + Services.prefs.clearUserPref("browser.safebrowsing.malware.enabled"); 1.68 + }); 1.69 + 1.70 + // Set download_block_table explicitly. 1.71 + Services.prefs.setCharPref("urlclassifier.downloadBlockTable", 1.72 + "goog-badbinurl-shavar"); 1.73 + do_register_cleanup(function() { 1.74 + Services.prefs.clearUserPref("urlclassifier.downloadBlockTable"); 1.75 + }); 1.76 + 1.77 + gHttpServ = new HttpServer(); 1.78 + gHttpServ.registerDirectory("/", do_get_cwd()); 1.79 + gHttpServ.registerPathHandler("/download", function(request, response) { 1.80 + do_throw("This test should never make a remote lookup"); 1.81 + }); 1.82 + gHttpServ.start(4444); 1.83 + 1.84 + run_next_test(); 1.85 +} 1.86 + 1.87 +function check_telemetry(aCount, 1.88 + aShouldBlockCount, 1.89 + aListCounts) { 1.90 + let count = Cc["@mozilla.org/base/telemetry;1"] 1.91 + .getService(Ci.nsITelemetry) 1.92 + .getHistogramById("APPLICATION_REPUTATION_COUNT") 1.93 + .snapshot(); 1.94 + do_check_eq(count.counts[1], aCount); 1.95 + let local = Cc["@mozilla.org/base/telemetry;1"] 1.96 + .getService(Ci.nsITelemetry) 1.97 + .getHistogramById("APPLICATION_REPUTATION_LOCAL") 1.98 + .snapshot(); 1.99 + do_check_eq(local.counts[ALLOW_LIST], aListCounts[ALLOW_LIST]); 1.100 + do_check_eq(local.counts[BLOCK_LIST], aListCounts[BLOCK_LIST]); 1.101 + do_check_eq(local.counts[NO_LIST], aListCounts[NO_LIST]); 1.102 + let shouldBlock = Cc["@mozilla.org/base/telemetry;1"] 1.103 + .getService(Ci.nsITelemetry) 1.104 + .getHistogramById("APPLICATION_REPUTATION_SHOULD_BLOCK") 1.105 + .snapshot(); 1.106 + // SHOULD_BLOCK = true 1.107 + do_check_eq(shouldBlock.counts[1], aShouldBlockCount); 1.108 + // Sanity check that SHOULD_BLOCK total adds up to the COUNT. 1.109 + do_check_eq(shouldBlock.counts[0] + shouldBlock.counts[1], aCount); 1.110 +} 1.111 + 1.112 +function get_telemetry_counts() { 1.113 + let count = Cc["@mozilla.org/base/telemetry;1"] 1.114 + .getService(Ci.nsITelemetry) 1.115 + .getHistogramById("APPLICATION_REPUTATION_COUNT") 1.116 + .snapshot(); 1.117 + let local = Cc["@mozilla.org/base/telemetry;1"] 1.118 + .getService(Ci.nsITelemetry) 1.119 + .getHistogramById("APPLICATION_REPUTATION_LOCAL") 1.120 + .snapshot(); 1.121 + let shouldBlock = Cc["@mozilla.org/base/telemetry;1"] 1.122 + .getService(Ci.nsITelemetry) 1.123 + .getHistogramById("APPLICATION_REPUTATION_SHOULD_BLOCK") 1.124 + .snapshot(); 1.125 + return { total: count.counts[1], 1.126 + shouldBlock: shouldBlock.counts[1], 1.127 + listCounts: local.counts }; 1.128 +} 1.129 + 1.130 +add_test(function test_nullSourceURI() { 1.131 + let counts = get_telemetry_counts(); 1.132 + gAppRep.queryReputation({ 1.133 + // No source URI 1.134 + fileSize: 12, 1.135 + }, function onComplete(aShouldBlock, aStatus) { 1.136 + do_check_eq(Cr.NS_ERROR_UNEXPECTED, aStatus); 1.137 + do_check_false(aShouldBlock); 1.138 + check_telemetry(counts.total + 1, counts.shouldBlock, counts.listCounts); 1.139 + run_next_test(); 1.140 + }); 1.141 +}); 1.142 + 1.143 +add_test(function test_nullCallback() { 1.144 + let counts = get_telemetry_counts(); 1.145 + try { 1.146 + gAppRep.queryReputation({ 1.147 + sourceURI: createURI("http://example.com"), 1.148 + fileSize: 12, 1.149 + }, null); 1.150 + do_throw("Callback cannot be null"); 1.151 + } catch (ex if ex.result == Cr.NS_ERROR_INVALID_POINTER) { 1.152 + // We don't even increment the count here, because there's no callback. 1.153 + check_telemetry(counts.total, counts.shouldBlock, counts.listCounts); 1.154 + run_next_test(); 1.155 + } 1.156 +}); 1.157 + 1.158 +add_test(function test_disabled() { 1.159 + let counts = get_telemetry_counts(); 1.160 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", ""); 1.161 + gAppRep.queryReputation({ 1.162 + sourceURI: createURI("http://example.com"), 1.163 + fileSize: 12, 1.164 + }, function onComplete(aShouldBlock, aStatus) { 1.165 + // We should be getting NS_ERROR_NOT_AVAILABLE if the service is disabled 1.166 + do_check_eq(Cr.NS_ERROR_NOT_AVAILABLE, aStatus); 1.167 + do_check_false(aShouldBlock); 1.168 + check_telemetry(counts.total + 1, counts.shouldBlock, counts.listCounts); 1.169 + run_next_test(); 1.170 + }); 1.171 +}); 1.172 + 1.173 +// Set up the local whitelist. 1.174 +add_test(function test_local_list() { 1.175 + // Construct a response with redirect urls. 1.176 + function processUpdateRequest() { 1.177 + let response = "n:1000\n"; 1.178 + for (let table in gTables) { 1.179 + response += "i:" + table + "\n"; 1.180 + for (let i = 0; i < gTables[table].length; ++i) { 1.181 + response += "u:" + gTables[table][i] + "\n"; 1.182 + } 1.183 + } 1.184 + do_print("Returning update response: " + response); 1.185 + return response; 1.186 + } 1.187 + gHttpServ.registerPathHandler("/downloads", function(request, response) { 1.188 + let buf = NetUtil.readInputStreamToString(request.bodyInputStream, 1.189 + request.bodyInputStream.available()); 1.190 + let blob = processUpdateRequest(); 1.191 + response.setHeader("Content-Type", 1.192 + "application/vnd.google.safebrowsing-update", false); 1.193 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.194 + response.bodyOutputStream.write(blob, blob.length); 1.195 + }); 1.196 + 1.197 + let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"] 1.198 + .getService(Ci.nsIUrlClassifierStreamUpdater); 1.199 + streamUpdater.updateUrl = "http://localhost:4444/downloads"; 1.200 + 1.201 + // Load up some update chunks for the safebrowsing server to serve. 1.202 + // This chunk contains the hash of whitelisted.com/. 1.203 + registerTableUpdate("goog-badbinurl-shavar", "data/block_digest.chunk"); 1.204 + // This chunk contains the hash of blocklisted.com/. 1.205 + registerTableUpdate("goog-downloadwhite-digest256", "data/digest.chunk"); 1.206 + 1.207 + // Download some updates, and don't continue until the downloads are done. 1.208 + function updateSuccess(aEvent) { 1.209 + // Timeout of n:1000 is constructed in processUpdateRequest above and 1.210 + // passed back in the callback in nsIUrlClassifierStreamUpdater on success. 1.211 + do_check_eq("1000", aEvent); 1.212 + do_print("All data processed"); 1.213 + run_next_test(); 1.214 + } 1.215 + // Just throw if we ever get an update or download error. 1.216 + function handleError(aEvent) { 1.217 + do_throw("We didn't download or update correctly: " + aEvent); 1.218 + } 1.219 + streamUpdater.downloadUpdates( 1.220 + "goog-downloadwhite-digest256,goog-badbinurl-shavar", 1.221 + "goog-downloadwhite-digest256,goog-badbinurl-shavar;\n", 1.222 + updateSuccess, handleError, handleError); 1.223 +}); 1.224 + 1.225 +add_test(function test_unlisted() { 1.226 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", 1.227 + "http://localhost:4444/download"); 1.228 + let counts = get_telemetry_counts(); 1.229 + let listCounts = counts.listCounts; 1.230 + listCounts[NO_LIST]++; 1.231 + gAppRep.queryReputation({ 1.232 + sourceURI: createURI("http://example.com"), 1.233 + fileSize: 12, 1.234 + }, function onComplete(aShouldBlock, aStatus) { 1.235 + do_check_eq(Cr.NS_OK, aStatus); 1.236 + do_check_false(aShouldBlock); 1.237 + check_telemetry(counts.total + 1, counts.shouldBlock, listCounts); 1.238 + run_next_test(); 1.239 + }); 1.240 +}); 1.241 + 1.242 +add_test(function test_local_blacklist() { 1.243 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", 1.244 + "http://localhost:4444/download"); 1.245 + let counts = get_telemetry_counts(); 1.246 + let listCounts = counts.listCounts; 1.247 + listCounts[BLOCK_LIST]++; 1.248 + gAppRep.queryReputation({ 1.249 + sourceURI: createURI("http://blocklisted.com"), 1.250 + fileSize: 12, 1.251 + }, function onComplete(aShouldBlock, aStatus) { 1.252 + do_check_eq(Cr.NS_OK, aStatus); 1.253 + do_check_true(aShouldBlock); 1.254 + check_telemetry(counts.total + 1, counts.shouldBlock + 1, listCounts); 1.255 + run_next_test(); 1.256 + }); 1.257 +}); 1.258 + 1.259 +add_test(function test_referer_blacklist() { 1.260 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", 1.261 + "http://localhost:4444/download"); 1.262 + let counts = get_telemetry_counts(); 1.263 + let listCounts = counts.listCounts; 1.264 + listCounts[BLOCK_LIST]++; 1.265 + gAppRep.queryReputation({ 1.266 + sourceURI: createURI("http://example.com"), 1.267 + referrerURI: createURI("http://blocklisted.com"), 1.268 + fileSize: 12, 1.269 + }, function onComplete(aShouldBlock, aStatus) { 1.270 + do_check_eq(Cr.NS_OK, aStatus); 1.271 + do_check_true(aShouldBlock); 1.272 + check_telemetry(counts.total + 1, counts.shouldBlock + 1, listCounts); 1.273 + run_next_test(); 1.274 + }); 1.275 +}); 1.276 + 1.277 +add_test(function test_blocklist_trumps_allowlist() { 1.278 + Services.prefs.setCharPref("browser.safebrowsing.appRepURL", 1.279 + "http://localhost:4444/download"); 1.280 + let counts = get_telemetry_counts(); 1.281 + let listCounts = counts.listCounts; 1.282 + listCounts[BLOCK_LIST]++; 1.283 + gAppRep.queryReputation({ 1.284 + sourceURI: createURI("http://whitelisted.com"), 1.285 + referrerURI: createURI("http://blocklisted.com"), 1.286 + fileSize: 12, 1.287 + }, function onComplete(aShouldBlock, aStatus) { 1.288 + do_check_eq(Cr.NS_OK, aStatus); 1.289 + do_check_true(aShouldBlock); 1.290 + check_telemetry(counts.total + 1, counts.shouldBlock + 1, listCounts); 1.291 + run_next_test(); 1.292 + }); 1.293 +});