Bug 427862: Don't use cached full-hash entries without a successful safebrowsing update. r=tony, a1.9=beltzner

This commit is contained in:
dcamp%mozilla.com 2008-04-15 22:39:45 +00:00
Родитель 15d7bc44a6
Коммит 2ea1e96a18
10 изменённых файлов: 401 добавлений и 62 удалений

Просмотреть файл

@ -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,
]);
}