зеркало из https://github.com/mozilla/pjs.git
Bug 427862: Don't use cached full-hash entries without a successful safebrowsing update. r=tony, a1.9=beltzner
This commit is contained in:
Родитель
15d7bc44a6
Коммит
2ea1e96a18
|
@ -623,6 +623,14 @@ pref("urlclassifier.alternate_error_page", "blocked");
|
|||
// The number of random entries to send with a gethash request.
|
||||
pref("urlclassifier.gethashnoise", 4);
|
||||
|
||||
// The list of tables that use the gethash request to confirm partial results.
|
||||
pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
|
||||
|
||||
// If an urlclassifier table has not been updated in this number of seconds,
|
||||
// a gethash request will be forced to check that the result is still in
|
||||
// the database.
|
||||
pref("urlclassifier.confirm-age", 2700);
|
||||
|
||||
// URL for checking the reason for a malware warning.
|
||||
pref("browser.safebrowsing.malware.reportURL", "http://www.stopbadware.org/reports/container?source=@APP_UA_NAME@&version=@APP_VERSION@&reportname=");
|
||||
|
||||
|
|
|
@ -89,7 +89,8 @@ function PROT_MalwareWarden() {
|
|||
};
|
||||
|
||||
try {
|
||||
dbService_.beginUpdate(listener, "");
|
||||
dbService_.beginUpdate(listener,
|
||||
"test-malware-simple,test-phish-simple", "");
|
||||
dbService_.beginStream("", "");
|
||||
dbService_.updateStream(testUpdate);
|
||||
dbService_.finishStream();
|
||||
|
|
|
@ -408,10 +408,16 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
|
|||
return;
|
||||
}
|
||||
|
||||
var tableList;
|
||||
var tableNames = {};
|
||||
for (var tableName in this.tablesData) {
|
||||
if (this.tablesData[tableName].needsUpdate)
|
||||
tableNames[tableName] = true;
|
||||
if (!tableList) {
|
||||
tableList = tableName;
|
||||
} else {
|
||||
tableList += "," + tableName;
|
||||
}
|
||||
}
|
||||
|
||||
var request = "";
|
||||
|
@ -444,7 +450,8 @@ PROT_ListManager.prototype.makeUpdateRequest_ = function(tableData) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!streamer.downloadUpdates(request,
|
||||
if (!streamer.downloadUpdates(tableList,
|
||||
request,
|
||||
this.keyManager_.getClientKey(),
|
||||
BindToObject(this.updateSuccess_, this),
|
||||
BindToObject(this.updateError_, this),
|
||||
|
|
|
@ -103,7 +103,7 @@ interface nsIUrlClassifierUpdateObserver : nsISupports {
|
|||
* It provides async methods for querying and updating the database. As the
|
||||
* methods complete, they call the callback function.
|
||||
*/
|
||||
[scriptable, uuid(aaf2c3b2-b7cd-4368-9d46-3fe92e5f78b1)]
|
||||
[scriptable, uuid(7aae3f3a-527d-488b-a448-45dca6db0e80)]
|
||||
interface nsIUrlClassifierDBService : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -162,10 +162,12 @@ interface nsIUrlClassifierDBService : nsISupports
|
|||
* is already an update in progress.
|
||||
*
|
||||
* @param updater The update observer tied to this update.
|
||||
* @param tables A comma-separated list of tables included in this update.
|
||||
* @param clientKey The client key for calculating an update's MAC,
|
||||
* or empty to ignore MAC.
|
||||
*/
|
||||
void beginUpdate(in nsIUrlClassifierUpdateObserver updater,
|
||||
in ACString tables,
|
||||
in ACString clientKey);
|
||||
|
||||
/**
|
||||
|
@ -227,7 +229,7 @@ interface nsIUrlClassifierDBService : nsISupports
|
|||
* Interface for the actual worker thread. Implementations of this need not
|
||||
* be thread aware and just work on the database.
|
||||
*/
|
||||
[scriptable, uuid(76d923e5-bbde-4292-ae35-16a67d04d524)]
|
||||
[scriptable, uuid(2af84c09-269e-4fc2-b28f-af56717db118)]
|
||||
interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
|
||||
{
|
||||
// Provide a way to forcibly close the db connection.
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
* downloading the whole update and then updating the sqlite database, we
|
||||
* update tables as the data is streaming in.
|
||||
*/
|
||||
[scriptable, uuid(1a1f8b01-4221-4897-b030-9d301b8b8cc9)]
|
||||
[scriptable, uuid(daf3038a-556c-47d3-a3d2-36caa9a762a0)]
|
||||
interface nsIUrlClassifierStreamUpdater : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -56,6 +56,8 @@ interface nsIUrlClassifierStreamUpdater : nsISupports
|
|||
* Try to download updates from updateUrl. Only one instance of this
|
||||
* runs at a time, so we return false if another instance is already
|
||||
* running.
|
||||
* @param aRequestTables Comma-separated list of tables included in this
|
||||
* update.
|
||||
* @param aRequestBody The body for the request.
|
||||
* @param aClientKey The client key for checking the update's MAC.
|
||||
* @param aSuccessCallback Called after a successful update.
|
||||
|
@ -63,7 +65,8 @@ interface nsIUrlClassifierStreamUpdater : nsISupports
|
|||
* @param aDownloadErrorCallback Called if we get an http error or a
|
||||
* connection refused error.
|
||||
*/
|
||||
boolean downloadUpdates(in ACString aRequestBody,
|
||||
boolean downloadUpdates(in ACString aRequestTables,
|
||||
in ACString aRequestBody,
|
||||
in ACString aClientKey,
|
||||
in nsIUrlClassifierCallback aSuccessCallback,
|
||||
in nsIUrlClassifierCallback aUpdateErrorCallback,
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
#include "nsIDirectoryService.h"
|
||||
|
@ -145,6 +146,11 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
|
|||
#define GETHASH_NOISE_PREF "urlclassifier.gethashnoise"
|
||||
#define GETHASH_NOISE_DEFAULT 4
|
||||
|
||||
#define GETHASH_TABLES_PREF "urlclassifier.gethashtables"
|
||||
|
||||
#define CONFIRM_AGE_PREF "urlclassifier.confirm-age"
|
||||
#define CONFIRM_AGE_DEFAULT_SEC (45 * 60)
|
||||
|
||||
class nsUrlClassifierDBServiceWorker;
|
||||
|
||||
// Singleton instance.
|
||||
|
@ -157,6 +163,26 @@ static nsIThread* gDbBackgroundThread = nsnull;
|
|||
// thread.
|
||||
static PRBool gShuttingDownThread = PR_FALSE;
|
||||
|
||||
static PRInt32 gFreshnessGuarantee = CONFIRM_AGE_DEFAULT_SEC;
|
||||
|
||||
static void
|
||||
SplitTables(const nsACString& str, nsTArray<nsCString>& tables)
|
||||
{
|
||||
tables.Clear();
|
||||
|
||||
nsACString::const_iterator begin, iter, end;
|
||||
str.BeginReading(begin);
|
||||
str.EndReading(end);
|
||||
while (begin != end) {
|
||||
iter = begin;
|
||||
FindCharInReadable(',', iter, end);
|
||||
tables.AppendElement(Substring(begin, iter));
|
||||
begin = iter;
|
||||
if (begin != end)
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Hash class implementation
|
||||
|
||||
|
@ -201,6 +227,10 @@ struct nsUrlClassifierHash
|
|||
memcpy(buf, str.BeginReading(), sHashSize);
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
}
|
||||
|
||||
const PRBool operator==(const self_type& hash) const {
|
||||
return (memcmp(buf, hash.buf, sizeof(buf)) == 0);
|
||||
}
|
||||
|
@ -335,7 +365,9 @@ nsUrlClassifierEntry::Clear()
|
|||
class nsUrlClassifierLookupResult
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierLookupResult() : mConfirmed(PR_FALSE), mNoise(PR_FALSE) {}
|
||||
nsUrlClassifierLookupResult() : mConfirmed(PR_FALSE), mNoise(PR_FALSE) {
|
||||
mLookupFragment.Clear();
|
||||
}
|
||||
~nsUrlClassifierLookupResult() {}
|
||||
|
||||
PRBool operator==(const nsUrlClassifierLookupResult &result) const {
|
||||
|
@ -1119,6 +1151,9 @@ private:
|
|||
nsCOMPtr<mozIStorageStatement> mGetTableNameStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mInsertTableIdStatement;
|
||||
|
||||
// Stores the last time a given table was updated.
|
||||
nsDataHashtable<nsCStringHashKey, PRInt64> mTableFreshness;
|
||||
|
||||
// We receive data in small chunks that may be broken in the middle of
|
||||
// a line. So we save the last partial line here.
|
||||
nsCString mPendingStreamUpdate;
|
||||
|
@ -1141,6 +1176,9 @@ private:
|
|||
PRUint32 mHashSize;
|
||||
PRUint32 mChunkLen;
|
||||
|
||||
// List of tables included in this update.
|
||||
nsTArray<nsCString> mUpdateTables;
|
||||
|
||||
nsCString mUpdateTable;
|
||||
PRUint32 mUpdateTableId;
|
||||
|
||||
|
@ -1247,6 +1285,8 @@ nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise)
|
|||
|
||||
ResetUpdate();
|
||||
|
||||
mTableFreshness.Init();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1392,6 +1432,8 @@ nsUrlClassifierDBServiceWorker::CheckKey(const nsACString& spec,
|
|||
if (!mMainStore.ReadStatement(mMainStore.LookupStatement(), entry))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
|
||||
for (PRUint32 i = 0; i < fragments.Length(); i++) {
|
||||
if (entry.Match(fragments[i])) {
|
||||
// If the entry doesn't contain a complete hash, we need to
|
||||
|
@ -1404,12 +1446,27 @@ nsUrlClassifierDBServiceWorker::CheckKey(const nsACString& spec,
|
|||
|
||||
result->mLookupFragment = fragments[i];
|
||||
result->mEntry = entry;
|
||||
// This is a confirmed result if we matched a complete
|
||||
// fragment.
|
||||
result->mConfirmed = entry.mHaveComplete;
|
||||
|
||||
// Fill in the table name.
|
||||
GetTableName(entry.mTableId, result->mTableName);
|
||||
|
||||
PRBool fresh;
|
||||
PRInt64 tableUpdateTime;
|
||||
if (mTableFreshness.Get(result->mTableName, &tableUpdateTime)) {
|
||||
LOG(("tableUpdateTime: %lld, now: %lld, freshnessGuarantee: %d\n",
|
||||
tableUpdateTime, now, gFreshnessGuarantee));
|
||||
fresh = ((now - tableUpdateTime) <= gFreshnessGuarantee);
|
||||
} else {
|
||||
LOG(("No expiration time for this table.\n"));
|
||||
fresh = PR_FALSE;
|
||||
}
|
||||
|
||||
// This is a confirmed result if we match a complete fragment in
|
||||
// an up-to-date table.
|
||||
result->mConfirmed = entry.mHaveComplete && fresh;
|
||||
|
||||
LOG(("Found a result. complete=%d, fresh=%d",
|
||||
entry.mHaveComplete, fresh));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2619,6 +2676,7 @@ nsUrlClassifierDBServiceWorker::ResetUpdate()
|
|||
mUpdateObserver = nsnull;
|
||||
mUpdateClientKey.Truncate();
|
||||
mResetRequested = PR_FALSE;
|
||||
mUpdateTables.Clear();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -2630,6 +2688,7 @@ nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
|
||||
const nsACString &tables,
|
||||
const nsACString &clientKey)
|
||||
{
|
||||
if (gShuttingDownThread)
|
||||
|
@ -2673,6 +2732,8 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse
|
|||
// forwarded updates.
|
||||
mPrimaryStream = PR_TRUE;
|
||||
|
||||
SplitTables(tables, mUpdateTables);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2871,6 +2932,21 @@ nsUrlClassifierDBServiceWorker::FinishUpdate()
|
|||
mUpdateObserver->UpdateError(mUpdateStatus);
|
||||
}
|
||||
|
||||
if (!mResetRequested) {
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) {
|
||||
LOG(("Successfully updated %s", mUpdateTables[i].get()));
|
||||
mTableFreshness.Put(mUpdateTables[i], now);
|
||||
}
|
||||
} else {
|
||||
for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) {
|
||||
LOG(("Failed updating %s", mUpdateTables[i].get()));
|
||||
mTableFreshness.Remove(mUpdateTables[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ResetUpdate() clears mResetRequested...
|
||||
PRBool resetRequested = mResetRequested;
|
||||
|
||||
|
@ -2892,6 +2968,8 @@ nsUrlClassifierDBServiceWorker::ResetDatabase()
|
|||
LOG(("nsUrlClassifierDBServiceWorker::ResetDatabase [%p]", this));
|
||||
ClearCachedChunkLists();
|
||||
|
||||
mTableFreshness.Clear();
|
||||
|
||||
nsresult rv = CloseDb();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -2912,6 +2990,11 @@ nsUrlClassifierDBServiceWorker::CancelUpdate()
|
|||
mConnection->RollbackTransaction();
|
||||
mUpdateObserver->UpdateError(mUpdateStatus);
|
||||
|
||||
for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) {
|
||||
LOG(("Failed updating %s", mUpdateTables[i].get()));
|
||||
mTableFreshness.Remove(mUpdateTables[i]);
|
||||
}
|
||||
|
||||
ResetStream();
|
||||
ResetUpdate();
|
||||
}
|
||||
|
@ -3212,24 +3295,36 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArray<nsUrlClassifierLookupResu
|
|||
mResults = results;
|
||||
mResults->Sort();
|
||||
|
||||
// Check the results for partial matches. Partial matches will need to be
|
||||
// completed.
|
||||
// Check the results entries that need to be completed.
|
||||
for (PRUint32 i = 0; i < results->Length(); i++) {
|
||||
nsUrlClassifierLookupResult& result = results->ElementAt(i);
|
||||
|
||||
// We will complete partial matches and matches that are stale.
|
||||
if (!result.mConfirmed) {
|
||||
nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
|
||||
if (mDBService->GetCompleter(result.mTableName,
|
||||
getter_AddRefs(completer))) {
|
||||
nsCAutoString partialHash;
|
||||
partialHash.Assign(reinterpret_cast<char*>(result.mEntry.mPartialHash.buf),
|
||||
PARTIAL_LENGTH);
|
||||
PRUint8 *buf =
|
||||
result.mEntry.mHavePartial ? result.mEntry.mPartialHash.buf
|
||||
: result.mEntry.mCompleteHash.buf;
|
||||
partialHash.Assign(reinterpret_cast<char*>(buf), PARTIAL_LENGTH);
|
||||
|
||||
nsresult rv = completer->Complete(partialHash, this);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mPendingCompletions++;
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
|
||||
// For tables with no hash completer, a complete hash match is
|
||||
// good enough, it doesn't need to be fresh. (we need the
|
||||
// mLookupFragment comparison to weed out noise entries, which
|
||||
// should never be confirmed).
|
||||
if (result.mEntry.mHaveComplete
|
||||
&& (result.mLookupFragment == result.mEntry.mCompleteHash)) {
|
||||
result.mConfirmed = PR_TRUE;
|
||||
} else {
|
||||
NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3271,49 +3366,50 @@ nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash,
|
|||
PRUint32 chunkId,
|
||||
PRBool verified)
|
||||
{
|
||||
LOG(("nsUrlClassifierLookupCallback::Completion [%p, %d]", this, verified));
|
||||
LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d, %d]",
|
||||
this, PromiseFlatCString(tableName).get(), chunkId, verified));
|
||||
nsUrlClassifierCompleteHash hash;
|
||||
hash.Assign(completeHash);
|
||||
|
||||
for (PRUint32 i = 0; i < mResults->Length(); i++) {
|
||||
nsUrlClassifierLookupResult& result = mResults->ElementAt(i);
|
||||
|
||||
if (!result.mEntry.mHaveComplete &&
|
||||
// First, see if this result can be used to update an entry.
|
||||
if (verified &&
|
||||
!result.mEntry.mHaveComplete &&
|
||||
hash.StartsWith(result.mEntry.mPartialHash) &&
|
||||
result.mTableName == tableName &&
|
||||
result.mEntry.mChunkId == chunkId) {
|
||||
// We have a completion for this entry. Fill it in...
|
||||
result.mEntry.SetHash(hash);
|
||||
|
||||
// ... and make sure that it was the entry we were looking for.
|
||||
if (result.mLookupFragment == hash)
|
||||
result.mConfirmed = PR_TRUE;
|
||||
|
||||
// If this result is guaranteed to come from our list provider,
|
||||
// we can cache the results.
|
||||
if (verified) {
|
||||
if (!mCacheResults) {
|
||||
mCacheResults = new nsTArray<nsUrlClassifierLookupResult>();
|
||||
if (!mCacheResults)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mCacheResults->AppendElement(result);
|
||||
if (!mCacheResults) {
|
||||
mCacheResults = new nsTArray<nsUrlClassifierLookupResult>();
|
||||
if (!mCacheResults)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
} else if (result.mLookupFragment == hash) {
|
||||
// The hash we got for this completion matches the hash we
|
||||
// looked up, but doesn't match the table/chunk id. This could
|
||||
// happen in rare cases where a given URL was moved between
|
||||
// lists or added/removed/re-added to the list in the time since
|
||||
// we've updated.
|
||||
//
|
||||
// Update the lookup result, but don't update the entry or try
|
||||
// caching the results of this completion, as it might confuse
|
||||
// things.
|
||||
result.mConfirmed = PR_TRUE;
|
||||
result.mTableName = tableName;
|
||||
|
||||
NS_WARNING("Accepting a gethash with an invalid table name or chunk id");
|
||||
mCacheResults->AppendElement(result);
|
||||
}
|
||||
|
||||
// Now, see if it verifies a lookup
|
||||
if (result.mLookupFragment == hash) {
|
||||
result.mConfirmed = PR_TRUE;
|
||||
|
||||
if (result.mTableName != tableName ||
|
||||
result.mEntry.mChunkId != chunkId) {
|
||||
// The hash we got for this completion matches the hash we
|
||||
// looked up, but doesn't match the table/chunk id. This could
|
||||
// happen in rare cases where a given URL was moved between
|
||||
// lists or added/removed/re-added to the list in the time since
|
||||
// we've updated.
|
||||
//
|
||||
// Update the lookup result, but don't update the entry or try
|
||||
// cache the results of this completion, as it might confuse
|
||||
// things.
|
||||
result.mTableName = tableName;
|
||||
NS_WARNING("Accepting a gethash with an invalid table name or chunk id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3496,6 +3592,20 @@ nsUrlClassifierDBService::Init()
|
|||
if (NS_FAILED(prefs->GetIntPref(GETHASH_NOISE_PREF, &gethashNoise))) {
|
||||
gethashNoise = GETHASH_NOISE_DEFAULT;
|
||||
}
|
||||
|
||||
nsXPIDLCString tmpstr;
|
||||
if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(tmpstr)))) {
|
||||
SplitTables(tmpstr, mGethashWhitelist);
|
||||
}
|
||||
|
||||
prefs->AddObserver(GETHASH_TABLES_PREF, this, PR_FALSE);
|
||||
|
||||
PRInt32 tmpint;
|
||||
rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
|
||||
PR_AtomicSet(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC);
|
||||
|
||||
prefs->AddObserver(CONFIRM_AGE_PREF, this, PR_FALSE);
|
||||
|
||||
}
|
||||
|
||||
// Start the background thread.
|
||||
|
@ -3656,6 +3766,7 @@ nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
|
||||
const nsACString &updateTables,
|
||||
const nsACString &clientKey)
|
||||
{
|
||||
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
|
||||
|
@ -3676,7 +3787,7 @@ nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
|
|||
getter_AddRefs(proxyObserver));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mWorkerProxy->BeginUpdate(proxyObserver, clientKey);
|
||||
return mWorkerProxy->BeginUpdate(proxyObserver, updateTables, clientKey);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -3749,6 +3860,10 @@ nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
|
|||
return PR_TRUE;
|
||||
}
|
||||
|
||||
if (!mGethashWhitelist.Contains(tableName)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
|
||||
completer));
|
||||
}
|
||||
|
@ -3769,6 +3884,16 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
|
|||
PRBool tmpbool;
|
||||
rv = prefs->GetBoolPref(CHECK_PHISHING_PREF, &tmpbool);
|
||||
mCheckPhishing = NS_SUCCEEDED(rv) ? tmpbool : CHECK_PHISHING_DEFAULT;
|
||||
} else if (NS_LITERAL_STRING(GETHASH_TABLES_PREF).Equals(aData)) {
|
||||
mGethashWhitelist.Clear();
|
||||
nsXPIDLCString val;
|
||||
if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(val)))) {
|
||||
SplitTables(val, mGethashWhitelist);
|
||||
}
|
||||
} else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
|
||||
PRInt32 tmpint;
|
||||
rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
|
||||
PR_AtomicSet(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "profile-before-change") ||
|
||||
!strcmp(aTopic, "xpcom-shutdown-threads")) {
|
||||
|
@ -3795,6 +3920,8 @@ nsUrlClassifierDBService::Shutdown()
|
|||
if (prefs) {
|
||||
prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
|
||||
prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
|
||||
prefs->RemoveObserver(GETHASH_TABLES_PREF, this);
|
||||
prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
|
|
@ -123,6 +123,9 @@ private:
|
|||
// updates, not to determine whether an update is still being
|
||||
// processed.
|
||||
PRBool mInUpdate;
|
||||
|
||||
// The list of tables that can use the default hash completer object.
|
||||
nsTArray<nsCString> mGethashWhitelist;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
|
||||
|
|
|
@ -167,6 +167,7 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::DownloadUpdates(
|
||||
const nsACString &aRequestTables,
|
||||
const nsACString &aRequestBody,
|
||||
const nsACString &aClientKey,
|
||||
nsIUrlClassifierCallback *aSuccessCallback,
|
||||
|
@ -208,7 +209,7 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
|
|||
mInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
rv = mDBService->BeginUpdate(this, aClientKey);
|
||||
rv = mDBService->BeginUpdate(this, aRequestTables, aClientKey);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG(("already updating, skipping update"));
|
||||
*_retval = PR_FALSE;
|
||||
|
|
|
@ -102,6 +102,10 @@ function buildPhishingUpdate(chunks, hashSize) {
|
|||
return buildUpdate({"test-phish-simple" : chunks}, hashSize);
|
||||
}
|
||||
|
||||
function buildMalwareUpdate(chunks, hashSize) {
|
||||
return buildUpdate({"test-malware-simple" : chunks}, hashSize);
|
||||
}
|
||||
|
||||
function buildBareUpdate(chunks, hashSize) {
|
||||
return buildUpdate({"" : chunks}, hashSize);
|
||||
}
|
||||
|
@ -125,13 +129,39 @@ function doSimpleUpdate(updateText, success, failure, clientKey) {
|
|||
updateSuccess: function(requestedTimeout) { success(requestedTimeout); }
|
||||
};
|
||||
|
||||
dbservice.beginUpdate(listener, clientKey);
|
||||
dbservice.beginUpdate(listener,
|
||||
"test-phish-simple,test-malware-simple",
|
||||
clientKey);
|
||||
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) { },
|
||||
streamCompleted: function() { },
|
||||
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
|
||||
|
@ -143,7 +173,8 @@ function doStreamUpdate(updateText, success, failure, downloadFailure, clientKey
|
|||
downloadFailure = failure;
|
||||
|
||||
streamUpdater.updateUrl = dataUpdate;
|
||||
streamUpdater.downloadUpdates("", clientKey, success, failure, downloadFailure);
|
||||
streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple", "",
|
||||
clientKey, success, failure, downloadFailure);
|
||||
}
|
||||
|
||||
var gAssertions = {
|
||||
|
|
|
@ -551,24 +551,180 @@ function testUncachedResults()
|
|||
});
|
||||
}
|
||||
|
||||
function testErrorList()
|
||||
{
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}],
|
||||
32);
|
||||
|
||||
var completer = installCompleter('test-phish-simple', [[1, addUrls]], []);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
"urlsExist" : addUrls,
|
||||
// These are complete urls, and will only be completed if the
|
||||
// list is stale.
|
||||
"completerQueried" : [completer, addUrls]
|
||||
};
|
||||
|
||||
// Apply the update.
|
||||
doStreamUpdate(update, function() {
|
||||
// Now the test-phish-simple and test-malware-simple tables are marked
|
||||
// as fresh. Fake an update failure to mark them stale.
|
||||
doErrorUpdate("test-phish-simple,test-malware-simple", function() {
|
||||
// Now the lists should be marked stale. Check assertions.
|
||||
checkAssertions(assertions, runNextTest);
|
||||
}, updateError);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
|
||||
function testStaleList()
|
||||
{
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}],
|
||||
32);
|
||||
|
||||
var completer = installCompleter('test-phish-simple', [[1, addUrls]], []);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
"urlsExist" : addUrls,
|
||||
// These are complete urls, and will only be completed if the
|
||||
// list is stale.
|
||||
"completerQueried" : [completer, addUrls]
|
||||
};
|
||||
|
||||
// Consider a match stale after one second.
|
||||
prefBranch.setIntPref("urlclassifier.confirm-age", 1);
|
||||
|
||||
// Apply the update.
|
||||
doStreamUpdate(update, function() {
|
||||
// Now the test-phish-simple and test-malware-simple tables are marked
|
||||
// as fresh. Wait three seconds to make sure the list is marked stale.
|
||||
new Timer(3000, function() {
|
||||
// Now the lists should be marked stale. Check assertions.
|
||||
checkAssertions(assertions, function() {
|
||||
prefBranch.setIntPref("urlclassifier.confirm-age", 2700);
|
||||
runNextTest();
|
||||
});
|
||||
}, updateError);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
// Same as testStaleList, but verifies that an empty response still
|
||||
// unconfirms the entry.
|
||||
function testStaleListEmpty()
|
||||
{
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}],
|
||||
32);
|
||||
|
||||
var completer = installCompleter('test-phish-simple', [], []);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
// None of these should match, because they won't be completed
|
||||
"urlsDontExist" : addUrls,
|
||||
// These are complete urls, and will only be completed if the
|
||||
// list is stale.
|
||||
"completerQueried" : [completer, addUrls]
|
||||
};
|
||||
|
||||
// Consider a match stale after one second.
|
||||
prefBranch.setIntPref("urlclassifier.confirm-age", 1);
|
||||
|
||||
// Apply the update.
|
||||
doStreamUpdate(update, function() {
|
||||
// Now the test-phish-simple and test-malware-simple tables are marked
|
||||
// as fresh. Wait three seconds to make sure the list is marked stale.
|
||||
new Timer(3000, function() {
|
||||
// Now the lists should be marked stale. Check assertions.
|
||||
checkAssertions(assertions, function() {
|
||||
prefBranch.setIntPref("urlclassifier.confirm-age", 2700);
|
||||
runNextTest();
|
||||
});
|
||||
}, updateError);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
|
||||
// Verify that different lists (test-phish-simple,
|
||||
// test-malware-simple) maintain their freshness separately.
|
||||
function testErrorListIndependent()
|
||||
{
|
||||
var phishUrls = [ "phish.com/a" ];
|
||||
var malwareUrls = [ "attack.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : phishUrls
|
||||
}],
|
||||
32);
|
||||
|
||||
update += buildMalwareUpdate(
|
||||
[
|
||||
{ "chunkNum" : 2,
|
||||
"urls" : malwareUrls
|
||||
}],
|
||||
32);
|
||||
|
||||
var completer = installCompleter('test-phish-simple', [[1, phishUrls]], []);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-malware-simple;a:2\ntest-phish-simple;a:1",
|
||||
"urlsExist" : phishUrls,
|
||||
"malwareUrlsExist" : malwareUrls,
|
||||
// Only this phishing urls should be completed, because only the phishing
|
||||
// urls will be stale.
|
||||
"completerQueried" : [completer, phishUrls]
|
||||
};
|
||||
|
||||
// Apply the update.
|
||||
doStreamUpdate(update, function() {
|
||||
// Now the test-phish-simple and test-malware-simple tables are
|
||||
// marked as fresh. Fake an update failure to mark *just*
|
||||
// phishing data as stale.
|
||||
doErrorUpdate("test-phish-simple", function() {
|
||||
// Now the lists should be marked stale. Check assertions.
|
||||
checkAssertions(assertions, runNextTest);
|
||||
}, updateError);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
runTests([
|
||||
testPartialAdds,
|
||||
testPartialAddsWithConflicts,
|
||||
testFalsePositives,
|
||||
testEmptyCompleter,
|
||||
testCompleterFailure,
|
||||
testMixedSizesSameDomain,
|
||||
testMixedSizesDifferentDomains,
|
||||
testInvalidHashSize,
|
||||
testWrongTable,
|
||||
testWrongChunk,
|
||||
testCachedResults,
|
||||
testCachedResultsWithSub,
|
||||
testCachedResultsWithExpire,
|
||||
testUncachedResults,
|
||||
testPartialAdds,
|
||||
testPartialAddsWithConflicts,
|
||||
testFalsePositives,
|
||||
testEmptyCompleter,
|
||||
testCompleterFailure,
|
||||
testMixedSizesSameDomain,
|
||||
testMixedSizesDifferentDomains,
|
||||
testInvalidHashSize,
|
||||
testWrongTable,
|
||||
testWrongChunk,
|
||||
testCachedResults,
|
||||
testCachedResultsWithSub,
|
||||
testCachedResultsWithExpire,
|
||||
testUncachedResults,
|
||||
testStaleList,
|
||||
testStaleListEmpty,
|
||||
testErrorList,
|
||||
testErrorListIndependent,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче