123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- this.EXPORTED_SYMBOLS = ["SafeBrowsing"];
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- const Cu = Components.utils;
- Cu.import("resource://gre/modules/Services.jsm");
- // Log only if browser.safebrowsing.debug is true
- function log(...stuff) {
- let logging = null;
- try {
- logging = Services.prefs.getBoolPref("browser.safebrowsing.debug");
- } catch(e) {
- return;
- }
- if (!logging) {
- return;
- }
- var d = new Date();
- let msg = "SafeBrowsing: " + d.toTimeString() + ": " + stuff.join(" ");
- dump(Services.urlFormatter.trimSensitiveURLs(msg) + "\n");
- }
- function getLists(prefName) {
- log("getLists: " + prefName);
- let pref = null;
- try {
- pref = Services.prefs.getCharPref(prefName);
- } catch(e) {
- return null;
- }
- // Splitting an empty string returns [''], we really want an empty array.
- if (!pref) {
- return [];
- }
- return pref.split(",")
- .map(function(value) { return value.trim(); });
- }
- const tablePreferences = [
- "urlclassifier.phishTable",
- "urlclassifier.malwareTable",
- "urlclassifier.downloadBlockTable",
- "urlclassifier.downloadAllowTable",
- "urlclassifier.trackingTable",
- "urlclassifier.trackingWhitelistTable",
- "urlclassifier.blockedTable"
- ];
- this.SafeBrowsing = {
- init: function() {
- if (this.initialized) {
- log("Already initialized");
- return;
- }
- Services.prefs.addObserver("browser.safebrowsing", this, false);
- Services.prefs.addObserver("privacy.trackingprotection", this, false);
- Services.prefs.addObserver("urlclassifier", this, false);
- this.readPrefs();
- this.addMozEntries();
- this.controlUpdateChecking();
- this.initialized = true;
- log("init() finished");
- },
- registerTableWithURLs: function(listname) {
- let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
- getService(Ci.nsIUrlListManager);
- let providerName = this.listToProvider[listname];
- let provider = this.providers[providerName];
- if (!providerName || !provider) {
- log("No provider info found for " + listname);
- log("Check browser.safebrowsing.provider.[google/mozilla].lists");
- return;
- }
- listManager.registerTable(listname, providerName, provider.updateURL, provider.gethashURL);
- },
- registerTables: function() {
- for (let i = 0; i < this.phishingLists.length; ++i) {
- this.registerTableWithURLs(this.phishingLists[i]);
- }
- for (let i = 0; i < this.malwareLists.length; ++i) {
- this.registerTableWithURLs(this.malwareLists[i]);
- }
- for (let i = 0; i < this.downloadBlockLists.length; ++i) {
- this.registerTableWithURLs(this.downloadBlockLists[i]);
- }
- for (let i = 0; i < this.downloadAllowLists.length; ++i) {
- this.registerTableWithURLs(this.downloadAllowLists[i]);
- }
- for (let i = 0; i < this.trackingProtectionLists.length; ++i) {
- this.registerTableWithURLs(this.trackingProtectionLists[i]);
- }
- for (let i = 0; i < this.trackingProtectionWhitelists.length; ++i) {
- this.registerTableWithURLs(this.trackingProtectionWhitelists[i]);
- }
- for (let i = 0; i < this.blockedLists.length; ++i) {
- this.registerTableWithURLs(this.blockedLists[i]);
- }
- },
- initialized: false,
- phishingEnabled: false,
- malwareEnabled: false,
- trackingEnabled: false,
- blockedEnabled: false,
- phishingLists: [],
- malwareLists: [],
- downloadBlockLists: [],
- downloadAllowLists: [],
- trackingProtectionLists: [],
- trackingProtectionWhitelists: [],
- blockedLists: [],
- updateURL: null,
- gethashURL: null,
- reportURL: null,
- getReportURL: function(kind, URI) {
- let pref;
- switch (kind) {
- case "Phish":
- pref = "browser.safebrowsing.reportPhishURL";
- break;
- case "PhishMistake":
- pref = "browser.safebrowsing.reportPhishMistakeURL";
- break;
- case "MalwareMistake":
- pref = "browser.safebrowsing.reportMalwareMistakeURL";
- break;
- default:
- let err = "SafeBrowsing getReportURL() called with unknown kind: " + kind;
- Components.utils.reportError(err);
- throw err;
- }
- let reportUrl = Services.urlFormatter.formatURLPref(pref);
- let pageUri = URI.clone();
- // Remove the query to avoid including potentially sensitive data
- if (pageUri instanceof Ci.nsIURL)
- pageUri.query = '';
- reportUrl += encodeURIComponent(pageUri.asciiSpec);
- return reportUrl;
- },
- observe: function(aSubject, aTopic, aData) {
- // skip nextupdatetime and lastupdatetime
- if (aData.indexOf("lastupdatetime") >= 0 || aData.indexOf("nextupdatetime") >= 0) {
- return;
- }
- this.readPrefs();
- },
- readPrefs: function() {
- log("reading prefs");
- this.debug = Services.prefs.getBoolPref("browser.safebrowsing.debug");
- this.phishingEnabled = Services.prefs.getBoolPref("browser.safebrowsing.phishing.enabled");
- this.malwareEnabled = Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
- this.trackingEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled") || Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled");
- this.blockedEnabled = Services.prefs.getBoolPref("browser.safebrowsing.blockedURIs.enabled");
- [this.phishingLists,
- this.malwareLists,
- this.downloadBlockLists,
- this.downloadAllowLists,
- this.trackingProtectionLists,
- this.trackingProtectionWhitelists,
- this.blockedLists] = tablePreferences.map(getLists);
- this.updateProviderURLs();
- this.registerTables();
- // XXX The listManager backend gets confused if this is called before the
- // lists are registered. So only call it here when a pref changes, and not
- // when doing initialization. I expect to refactor this later, so pardon the hack.
- if (this.initialized) {
- this.controlUpdateChecking();
- }
- },
- updateProviderURLs: function() {
- try {
- var clientID = Services.prefs.getCharPref("browser.safebrowsing.id");
- } catch(e) {
- clientID = Services.appinfo.name;
- }
- log("initializing safe browsing URLs, client id", clientID);
- // Get the different providers
- let branch = Services.prefs.getBranch("browser.safebrowsing.provider.");
- let children = branch.getChildList("", {});
- this.providers = {};
- this.listToProvider = {};
- for (let child of children) {
- log("Child: " + child);
- let prefComponents = child.split(".");
- let providerName = prefComponents[0];
- this.providers[providerName] = {};
- }
- if (this.debug) {
- let providerStr = "";
- Object.keys(this.providers).forEach(function(provider) {
- if (providerStr === "") {
- providerStr = provider;
- } else {
- providerStr += ", " + provider;
- }
- });
- log("Providers: " + providerStr);
- }
- Object.keys(this.providers).forEach(function(provider) {
- let updateURL = Services.urlFormatter.formatURLPref(
- "browser.safebrowsing.provider." + provider + ".updateURL");
- let gethashURL = Services.urlFormatter.formatURLPref(
- "browser.safebrowsing.provider." + provider + ".gethashURL");
- updateURL = updateURL.replace("SAFEBROWSING_ID", clientID);
- gethashURL = gethashURL.replace("SAFEBROWSING_ID", clientID);
- log("Provider: " + provider + " updateURL=" + updateURL);
- log("Provider: " + provider + " gethashURL=" + gethashURL);
- // Urls used to update DB
- this.providers[provider].updateURL = updateURL;
- this.providers[provider].gethashURL = gethashURL;
- // Get lists this provider manages
- let lists = getLists("browser.safebrowsing.provider." + provider + ".lists");
- if (lists) {
- lists.forEach(function(list) {
- this.listToProvider[list] = provider;
- }, this);
- } else {
- log("Update URL given but no lists managed for provider: " + provider);
- }
- }, this);
- },
- controlUpdateChecking: function() {
- log("phishingEnabled:", this.phishingEnabled, "malwareEnabled:",
- this.malwareEnabled, "trackingEnabled:", this.trackingEnabled,
- "blockedEnabled:", this.blockedEnabled);
- let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
- getService(Ci.nsIUrlListManager);
- for (let i = 0; i < this.phishingLists.length; ++i) {
- if (this.phishingEnabled) {
- listManager.enableUpdate(this.phishingLists[i]);
- } else {
- listManager.disableUpdate(this.phishingLists[i]);
- }
- }
- for (let i = 0; i < this.malwareLists.length; ++i) {
- if (this.malwareEnabled) {
- listManager.enableUpdate(this.malwareLists[i]);
- } else {
- listManager.disableUpdate(this.malwareLists[i]);
- }
- }
- for (let i = 0; i < this.downloadBlockLists.length; ++i) {
- if (this.malwareEnabled) {
- listManager.enableUpdate(this.downloadBlockLists[i]);
- } else {
- listManager.disableUpdate(this.downloadBlockLists[i]);
- }
- }
- for (let i = 0; i < this.downloadAllowLists.length; ++i) {
- if (this.malwareEnabled) {
- listManager.enableUpdate(this.downloadAllowLists[i]);
- } else {
- listManager.disableUpdate(this.downloadAllowLists[i]);
- }
- }
- for (let i = 0; i < this.trackingProtectionLists.length; ++i) {
- if (this.trackingEnabled) {
- listManager.enableUpdate(this.trackingProtectionLists[i]);
- } else {
- listManager.disableUpdate(this.trackingProtectionLists[i]);
- }
- }
- for (let i = 0; i < this.trackingProtectionWhitelists.length; ++i) {
- if (this.trackingEnabled) {
- listManager.enableUpdate(this.trackingProtectionWhitelists[i]);
- } else {
- listManager.disableUpdate(this.trackingProtectionWhitelists[i]);
- }
- }
- for (let i = 0; i < this.blockedLists.length; ++i) {
- if (this.blockedEnabled) {
- listManager.enableUpdate(this.blockedLists[i]);
- } else {
- listManager.disableUpdate(this.blockedLists[i]);
- }
- }
- listManager.maybeToggleUpdateChecking();
- },
- addMozEntries: function() {
- // Add test entries to the DB.
- // XXX bug 779008 - this could be done by DB itself?
- const phishURL = "itisatrap.org/firefox/its-a-trap.html";
- const malwareURL = "itisatrap.org/firefox/its-an-attack.html";
- const unwantedURL = "itisatrap.org/firefox/unwanted.html";
- const trackerURLs = [
- "trackertest.org/",
- "itisatracker.org/",
- ];
- const whitelistURL = "itisatrap.org/?resource=itisatracker.org";
- const blockedURL = "itisatrap.org/firefox/blocked.html";
- const flashDenyURL = "flashblock.itisatrap.org/";
- const flashDenyExceptURL = "except.flashblock.itisatrap.org/";
- const flashAllowURL = "flashallow.itisatrap.org/";
- const flashAllowExceptURL = "except.flashallow.itisatrap.org/";
- const flashSubDocURL = "flashsubdoc.itisatrap.org/";
- const flashSubDocExceptURL = "except.flashsubdoc.itisatrap.org/";
- let update = "n:1000\ni:test-malware-simple\nad:1\n" +
- "a:1:32:" + malwareURL.length + "\n" +
- malwareURL + "\n";
- update += "n:1000\ni:test-phish-simple\nad:1\n" +
- "a:1:32:" + phishURL.length + "\n" +
- phishURL + "\n";
- update += "n:1000\ni:test-unwanted-simple\nad:1\n" +
- "a:1:32:" + unwantedURL.length + "\n" +
- unwantedURL + "\n";
- update += "n:1000\ni:test-track-simple\n" +
- "ad:" + trackerURLs.length + "\n";
- trackerURLs.forEach((trackerURL, i) => {
- update += "a:" + (i + 1) + ":32:" + trackerURL.length + "\n" +
- trackerURL + "\n";
- });
- update += "n:1000\ni:test-trackwhite-simple\nad:1\n" +
- "a:1:32:" + whitelistURL.length + "\n" +
- whitelistURL;
- update += "n:1000\ni:test-block-simple\nad:1\n" +
- "a:1:32:" + blockedURL.length + "\n" +
- blockedURL;
- update += "n:1000\ni:test-flash-simple\nad:1\n" +
- "a:1:32:" + flashDenyURL.length + "\n" +
- flashDenyURL;
- update += "n:1000\ni:testexcept-flash-simple\nad:1\n" +
- "a:1:32:" + flashDenyExceptURL.length + "\n" +
- flashDenyExceptURL;
- update += "n:1000\ni:test-flashallow-simple\nad:1\n" +
- "a:1:32:" + flashAllowURL.length + "\n" +
- flashAllowURL;
- update += "n:1000\ni:testexcept-flashallow-simple\nad:1\n" +
- "a:1:32:" + flashAllowExceptURL.length + "\n" +
- flashAllowExceptURL;
- update += "n:1000\ni:test-flashsubdoc-simple\nad:1\n" +
- "a:1:32:" + flashSubDocURL.length + "\n" +
- flashSubDocURL;
- update += "n:1000\ni:testexcept-flashsubdoc-simple\nad:1\n" +
- "a:1:32:" + flashSubDocExceptURL.length + "\n" +
- flashSubDocExceptURL;
- log("addMozEntries:", update);
- let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
- getService(Ci.nsIUrlClassifierDBService);
- // nsIUrlClassifierUpdateObserver
- let dummyListener = {
- updateUrlRequested: function() { },
- streamFinished: function() { },
- // We notify observers when we're done in order to be able to make perf
- // test results more consistent
- updateError: function() {
- Services.obs.notifyObservers(db, "mozentries-update-finished", "error");
- },
- updateSuccess: function() {
- Services.obs.notifyObservers(db, "mozentries-update-finished", "success");
- }
- };
- try {
- let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,test-flash-simple,testexcept-flash-simple,test-flashallow-simple,testexcept-flashallow-simple,test-flashsubdoc-simple,testexcept-flashsubdoc-simple";
- db.beginUpdate(dummyListener, tables, "");
- db.beginStream("", "");
- db.updateStream(update);
- db.finishStream();
- db.finishUpdate();
- } catch(ex) {
- // beginUpdate will throw harmlessly if there's an existing update in progress, ignore failures.
- log("addMozEntries failed!", ex);
- Services.obs.notifyObservers(db, "mozentries-update-finished", "exception");
- }
- },
- addMozEntriesFinishedPromise: new Promise(resolve => {
- let finished = (subject, topic, data) => {
- Services.obs.removeObserver(finished, "mozentries-update-finished");
- if (data == "error") {
- Cu.reportError("addMozEntries failed to update the db!");
- }
- resolve();
- };
- Services.obs.addObserver(finished, "mozentries-update-finished", false);
- }),
- };
|