Index: components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc |
diff --git a/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc b/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc |
index edf130ac9dc24a607fddf176e6e960b100cdfe42..6923ee0db0393b5de1ebbdbbdb3bef88690c9983 100644 |
--- a/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc |
+++ b/components/safe_browsing_db/v4_get_hash_protocol_manager_unittest.cc |
@@ -9,6 +9,7 @@ |
#include "base/base64.h" |
#include "base/memory/ptr_util.h" |
+#include "base/run_loop.h" |
#include "base/strings/stringprintf.h" |
#include "base/test/simple_test_clock.h" |
#include "base/time/time.h" |
@@ -19,7 +20,7 @@ |
#include "net/base/load_flags.h" |
#include "net/base/net_errors.h" |
#include "net/url_request/test_url_fetcher_factory.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/platform_test.h" |
using base::Time; |
using base::TimeDelta; |
@@ -34,37 +35,77 @@ const char kKeyParam[] = "test_key_param"; |
namespace safe_browsing { |
-class SafeBrowsingV4GetHashProtocolManagerTest : public testing::Test { |
- protected: |
+namespace { |
+ |
+struct KeyValue { |
+ std::string key; |
+ std::string value; |
+ |
+ explicit KeyValue(const std::string key, const std::string value) |
+ : key(key), value(value){}; |
+ explicit KeyValue(const KeyValue& other) = default; |
+ |
+ private: |
+ KeyValue(); |
+}; |
+ |
+struct ResponseInfo { |
+ FullHash full_hash; |
+ UpdateListIdentifier list_id; |
+ std::vector<KeyValue> key_values; |
+ |
+ explicit ResponseInfo(FullHash full_hash, UpdateListIdentifier list_id) |
+ : full_hash(full_hash), list_id(list_id){}; |
+ explicit ResponseInfo(const ResponseInfo& other) |
+ : full_hash(other.full_hash), |
+ list_id(other.list_id), |
+ key_values(other.key_values){}; |
+ |
+ private: |
+ ResponseInfo(); |
+}; |
+ |
+} // namespace |
+ |
+class V4GetHashProtocolManagerTest : public PlatformTest { |
+ public: |
+ void SetUp() override { |
+ PlatformTest::SetUp(); |
+ callback_called_ = false; |
+ } |
+ |
std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager() { |
V4ProtocolConfig config; |
config.client_name = kClient; |
config.version = kAppVer; |
config.key_param = kKeyParam; |
- return std::unique_ptr<V4GetHashProtocolManager>( |
- V4GetHashProtocolManager::Create(NULL, config)); |
+ base::hash_set<UpdateListIdentifier> stores_to_look( |
+ {GetUrlMalwareId(), GetChromeUrlApiId()}); |
+ return V4GetHashProtocolManager::Create(NULL, stores_to_look, config); |
} |
- std::string GetStockV4HashResponse() { |
- FindFullHashesResponse res; |
- res.mutable_negative_cache_duration()->set_seconds(600); |
- ThreatMatch* m = res.add_matches(); |
- m->set_threat_type(API_ABUSE); |
- m->set_platform_type(CHROME_PLATFORM); |
- m->set_threat_entry_type(URL); |
- m->mutable_cache_duration()->set_seconds(300); |
- m->mutable_threat()->set_hash( |
- SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); |
- ThreatEntryMetadata::MetadataEntry* e = |
- m->mutable_threat_entry_metadata()->add_entries(); |
- e->set_key("permission"); |
- e->set_value("NOTIFICATIONS"); |
+ static void SetupFetcherToReturnOKResponse( |
+ const net::TestURLFetcherFactory& factory, |
+ const std::vector<ResponseInfo>& infos) { |
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); |
+ DCHECK(fetcher); |
+ fetcher->set_status(net::URLRequestStatus()); |
+ fetcher->set_response_code(200); |
+ fetcher->SetResponseString(GetV4HashResponse(infos)); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ } |
- // Serialize. |
- std::string res_data; |
- res.SerializeToString(&res_data); |
+ static std::vector<ResponseInfo> GetStockV4HashResponseInfos() { |
+ ResponseInfo info(FullHash("Everything's shiny, Cap'n."), |
+ GetChromeUrlApiId()); |
+ info.key_values.emplace_back("permission", "NOTIFICATIONS"); |
+ std::vector<ResponseInfo> infos; |
+ infos.push_back(info); |
+ return infos; |
+ } |
- return res_data; |
+ static std::string GetStockV4HashResponse() { |
+ return GetV4HashResponse(GetStockV4HashResponseInfos()); |
} |
void SetTestClock(base::Time now, V4GetHashProtocolManager* pm) { |
@@ -72,37 +113,68 @@ class SafeBrowsingV4GetHashProtocolManagerTest : public testing::Test { |
clock->SetNow(now); |
pm->SetClockForTests(base::WrapUnique(clock)); |
} |
-}; |
-void ValidateGetV4HashResults( |
- const std::vector<SBFullHashResult>& expected_full_hashes, |
- const base::Time& expected_cache_expire, |
- const std::vector<SBFullHashResult>& full_hashes, |
- const base::Time& cache_expire) { |
- EXPECT_EQ(expected_cache_expire, cache_expire); |
- ASSERT_EQ(expected_full_hashes.size(), full_hashes.size()); |
- |
- for (unsigned int i = 0; i < expected_full_hashes.size(); ++i) { |
- const SBFullHashResult& expected = expected_full_hashes[i]; |
- const SBFullHashResult& actual = full_hashes[i]; |
- EXPECT_TRUE(SBFullHashEqual(expected.hash, actual.hash)); |
- EXPECT_EQ(expected.metadata, actual.metadata); |
- EXPECT_EQ(expected.cache_expire_after, actual.cache_expire_after); |
+ void ValidateGetV4ApiResults(const ThreatMetadata& expected_md, |
+ const ThreatMetadata& actual_md) { |
+ EXPECT_EQ(expected_md, actual_md); |
+ callback_called_ = true; |
} |
-} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
- TestGetHashErrorHandlingNetwork) { |
+ void ValidateGetV4HashResults( |
+ const std::vector<FullHashInfo>& expected_results, |
+ const std::vector<FullHashInfo>& actual_results) { |
+ EXPECT_EQ(expected_results.size(), actual_results.size()); |
+ for (size_t i = 0; i < actual_results.size(); i++) { |
+ EXPECT_TRUE(expected_results[i] == actual_results[i]); |
+ } |
+ callback_called_ = true; |
+ } |
+ |
+ bool callback_called() { return callback_called_; } |
+ |
+ private: |
+ static std::string GetV4HashResponse( |
+ std::vector<ResponseInfo> response_infos) { |
+ FindFullHashesResponse res; |
+ res.mutable_negative_cache_duration()->set_seconds(600); |
+ for (const ResponseInfo& info : response_infos) { |
+ ThreatMatch* m = res.add_matches(); |
+ m->set_platform_type(info.list_id.platform_type); |
+ m->set_threat_entry_type(info.list_id.threat_entry_type); |
+ m->set_threat_type(info.list_id.threat_type); |
+ m->mutable_cache_duration()->set_seconds(300); |
+ m->mutable_threat()->set_hash(info.full_hash); |
+ |
+ for (const KeyValue& key_value : info.key_values) { |
+ ThreatEntryMetadata::MetadataEntry* e = |
+ m->mutable_threat_entry_metadata()->add_entries(); |
+ e->set_key(key_value.key); |
+ e->set_value(key_value.value); |
+ } |
+ } |
+ |
+ // Serialize. |
+ std::string res_data; |
+ res.SerializeToString(&res_data); |
+ |
+ return res_data; |
+ } |
+ |
+ bool callback_called_; |
+}; |
+ |
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingNetwork) { |
net::TestURLFetcherFactory factory; |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
- std::vector<SBPrefix> prefixes; |
- std::vector<SBFullHashResult> expected_full_hashes; |
- base::Time expected_cache_expire; |
- |
- pm->GetFullHashesWithApis( |
- prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, |
- expected_cache_expire)); |
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; |
Nathan Parker
2016/09/09 21:26:22
nit: This is a long type name and a long var name.
vakh (use Gerrit instead)
2016/09/09 23:25:18
If you don't mind it too much, I actually prefer t
|
+ full_hash_to_store_and_hash_prefixes[FullHash("AFullHash")].push_back( |
+ StoreAndHashPrefix(GetUrlSocEngId(), HashPrefix("AHashPrefix"))); |
Nathan Parker
2016/09/09 21:26:22
Is it ok to have the prefix not match the full has
vakh (use Gerrit instead)
2016/09/09 23:25:18
In general not. But in this case it is irrelevant
|
+ std::vector<FullHashInfo> expected_results; |
+ pm->GetFullHashes( |
+ full_hash_to_store_and_hash_prefixes, |
+ base::Bind(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults, |
+ base::Unretained(this), expected_results)); |
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); |
DCHECK(fetcher); |
@@ -116,20 +188,21 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
// Should have recorded one error, but back off multiplier is unchanged. |
EXPECT_EQ(1ul, pm->gethash_error_count_); |
EXPECT_EQ(1ul, pm->gethash_back_off_mult_); |
+ EXPECT_TRUE(callback_called()); |
} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
- TestGetHashErrorHandlingResponseCode) { |
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingResponseCode) { |
net::TestURLFetcherFactory factory; |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
- std::vector<SBPrefix> prefixes; |
- std::vector<SBFullHashResult> expected_full_hashes; |
- base::Time expected_cache_expire; |
- |
- pm->GetFullHashesWithApis( |
- prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, |
- expected_cache_expire)); |
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; |
+ full_hash_to_store_and_hash_prefixes[FullHash("AFullHash")].push_back( |
+ StoreAndHashPrefix(GetUrlSocEngId(), HashPrefix("AHashPrefix"))); |
+ std::vector<FullHashInfo> expected_results; |
+ pm->GetFullHashes( |
+ full_hash_to_store_and_hash_prefixes, |
+ base::Bind(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults, |
+ base::Unretained(this), expected_results)); |
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); |
DCHECK(fetcher); |
@@ -142,81 +215,106 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
// Should have recorded one error, but back off multiplier is unchanged. |
EXPECT_EQ(1ul, pm->gethash_error_count_); |
EXPECT_EQ(1ul, pm->gethash_back_off_mult_); |
+ EXPECT_TRUE(callback_called()); |
} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, TestGetHashErrorHandlingOK) { |
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashErrorHandlingOK) { |
net::TestURLFetcherFactory factory; |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
base::Time now = base::Time::UnixEpoch(); |
SetTestClock(now, pm.get()); |
- std::vector<SBPrefix> prefixes; |
- std::vector<SBFullHashResult> expected_full_hashes; |
- SBFullHashResult hash_result; |
- hash_result.hash = SBFullHashForString("Everything's shiny, Cap'n."); |
- hash_result.metadata.api_permissions.insert("NOTIFICATIONS"); |
- hash_result.cache_expire_after = now + base::TimeDelta::FromSeconds(300); |
- expected_full_hashes.push_back(hash_result); |
- base::Time expected_cache_expire = now + base::TimeDelta::FromSeconds(600); |
+ HashPrefix prefix("Everything"); |
+ FullHash full_hash("Everything's shiny, Cap'n."); |
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; |
+ full_hash_to_store_and_hash_prefixes[full_hash].push_back( |
+ StoreAndHashPrefix(GetChromeUrlApiId(), prefix)); |
+ std::vector<FullHashInfo> expected_results; |
+ FullHashInfo fhi(full_hash, GetChromeUrlApiId(), |
+ now + base::TimeDelta::FromSeconds(300)); |
+ fhi.metadata.api_permissions.insert("NOTIFICATIONS"); |
+ expected_results.push_back(fhi); |
- pm->GetFullHashesWithApis( |
- prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, |
- expected_cache_expire)); |
+ pm->GetFullHashes( |
+ full_hash_to_store_and_hash_prefixes, |
+ base::Bind(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults, |
+ base::Unretained(this), expected_results)); |
- net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); |
- DCHECK(fetcher); |
- fetcher->set_status(net::URLRequestStatus()); |
- fetcher->set_response_code(200); |
- fetcher->SetResponseString(GetStockV4HashResponse()); |
- fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ SetupFetcherToReturnOKResponse(factory, GetStockV4HashResponseInfos()); |
// No error, back off multiplier is unchanged. |
EXPECT_EQ(0ul, pm->gethash_error_count_); |
EXPECT_EQ(1ul, pm->gethash_back_off_mult_); |
+ |
+ // Verify the state of the cache. |
+ const FullHashCache* cache = pm->full_hash_cache_for_tests(); |
+ // Check the cache. |
+ ASSERT_EQ(1u, cache->size()); |
+ EXPECT_EQ(1u, cache->count(prefix)); |
+ const CachedHashPrefixInfo& cached_result = cache->at(prefix); |
+ EXPECT_EQ(cached_result.negative_ttl, |
+ now + base::TimeDelta::FromSeconds(600)); |
+ ASSERT_EQ(1u, cached_result.full_hash_infos.size()); |
+ EXPECT_EQ(FullHash("Everything's shiny, Cap'n."), |
+ cached_result.full_hash_infos[0].full_hash); |
+ EXPECT_TRUE(callback_called()); |
} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, TestGetHashRequest) { |
+TEST_F(V4GetHashProtocolManagerTest, |
+ TestResultsNotCachedForNegativeCacheDuration) { |
+ net::TestURLFetcherFactory factory; |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
+ HashPrefix prefix("Everything"); |
+ std::vector<HashPrefix> prefixes_requested({prefix}); |
+ base::Time negative_cache_expire; |
+ FullHash full_hash("Everything's shiny, Cap'n."); |
+ std::vector<FullHashInfo> fhis; |
+ fhis.emplace_back(full_hash, GetChromeUrlApiId(), base::Time::UnixEpoch()); |
+ |
+ pm->UpdateCache(prefixes_requested, fhis, negative_cache_expire); |
+ |
+ // Verify the state of the cache. |
+ const FullHashCache* cache = pm->full_hash_cache_for_tests(); |
+ // Check the cache. |
+ EXPECT_EQ(0u, cache->size()); |
+} |
+ |
+TEST_F(V4GetHashProtocolManagerTest, TestGetHashRequest) { |
+ HashPrefix one = "hashone"; |
+ HashPrefix two = "hashtwo"; |
+ std::vector<HashPrefix> prefixes_to_request = {one, two}; |
+ |
FindFullHashesRequest req; |
ThreatInfo* info = req.mutable_threat_info(); |
+ info->add_threat_types(MALWARE_THREAT); |
info->add_threat_types(API_ABUSE); |
+ |
+ info->add_platform_types(GetCurrentPlatformType()); |
info->add_platform_types(CHROME_PLATFORM); |
+ |
info->add_threat_entry_types(URL); |
- SBPrefix one = 1u; |
- SBPrefix two = 2u; |
- SBPrefix three = 3u; |
- std::string hash(reinterpret_cast<const char*>(&one), sizeof(SBPrefix)); |
- info->add_threat_entries()->set_hash(hash); |
- hash.clear(); |
- hash.append(reinterpret_cast<const char*>(&two), sizeof(SBPrefix)); |
- info->add_threat_entries()->set_hash(hash); |
- hash.clear(); |
- hash.append(reinterpret_cast<const char*>(&three), sizeof(SBPrefix)); |
- info->add_threat_entries()->set_hash(hash); |
+ info->add_threat_entries()->set_hash(one); |
+ info->add_threat_entries()->set_hash(two); |
// Serialize and Base64 encode. |
std::string req_data, req_base64; |
req.SerializeToString(&req_data); |
base::Base64Encode(req_data, &req_base64); |
- std::vector<PlatformType> platform; |
- platform.push_back(CHROME_PLATFORM); |
- std::vector<SBPrefix> prefixes; |
- prefixes.push_back(one); |
- prefixes.push_back(two); |
- prefixes.push_back(three); |
- EXPECT_EQ(req_base64, pm->GetHashRequest(prefixes, platform, API_ABUSE)); |
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
+ EXPECT_EQ(req_base64, pm->GetHashRequest(prefixes_to_request)); |
} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, TestParseHashResponse) { |
+TEST_F(V4GetHashProtocolManagerTest, TestParseHashResponse) { |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
base::Time now = base::Time::UnixEpoch(); |
SetTestClock(now, pm.get()); |
+ FullHash full_hash("Everything's shiny, Cap'n."); |
FindFullHashesResponse res; |
res.mutable_negative_cache_duration()->set_seconds(600); |
res.mutable_minimum_wait_duration()->set_seconds(400); |
@@ -225,8 +323,7 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, TestParseHashResponse) { |
m->set_platform_type(CHROME_PLATFORM); |
m->set_threat_entry_type(URL); |
m->mutable_cache_duration()->set_seconds(300); |
- m->mutable_threat()->set_hash( |
- SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); |
+ m->mutable_threat()->set_hash(full_hash); |
ThreatEntryMetadata::MetadataEntry* e = |
m->mutable_threat_entry_metadata()->add_entries(); |
e->set_key("permission"); |
@@ -236,24 +333,23 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, TestParseHashResponse) { |
std::string res_data; |
res.SerializeToString(&res_data); |
- std::vector<SBFullHashResult> full_hashes; |
+ std::vector<FullHashInfo> full_hash_infos; |
base::Time cache_expire; |
- EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hashes, &cache_expire)); |
+ EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire)); |
EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
- EXPECT_EQ(1ul, full_hashes.size()); |
- EXPECT_TRUE(SBFullHashEqual(SBFullHashForString("Everything's shiny, Cap'n."), |
- full_hashes[0].hash)); |
- EXPECT_EQ(1ul, full_hashes[0].metadata.api_permissions.size()); |
- EXPECT_EQ(1ul, |
- full_hashes[0].metadata.api_permissions.count("NOTIFICATIONS")); |
- EXPECT_EQ(now + |
- base::TimeDelta::FromSeconds(300), full_hashes[0].cache_expire_after); |
+ ASSERT_EQ(1ul, full_hash_infos.size()); |
+ const FullHashInfo& fhi = full_hash_infos[0]; |
+ EXPECT_EQ(full_hash, fhi.full_hash); |
+ EXPECT_EQ(GetChromeUrlApiId(), fhi.list_id); |
+ EXPECT_EQ(1ul, fhi.metadata.api_permissions.size()); |
+ EXPECT_EQ(1ul, fhi.metadata.api_permissions.count("NOTIFICATIONS")); |
+ EXPECT_EQ(now + base::TimeDelta::FromSeconds(300), fhi.positive_ttl); |
EXPECT_EQ(now + base::TimeDelta::FromSeconds(400), pm->next_gethash_time_); |
} |
// Adds an entry with an ignored ThreatEntryType. |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
+TEST_F(V4GetHashProtocolManagerTest, |
TestParseHashResponseWrongThreatEntryType) { |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
@@ -268,96 +364,117 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
std::string res_data; |
res.SerializeToString(&res_data); |
- std::vector<SBFullHashResult> full_hashes; |
+ std::vector<FullHashInfo> full_hash_infos; |
base::Time cache_expire; |
- EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_expire)); |
+ EXPECT_FALSE( |
+ pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire)); |
EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
// There should be no hash results. |
- EXPECT_EQ(0ul, full_hashes.size()); |
+ EXPECT_EQ(0ul, full_hash_infos.size()); |
} |
// Adds entries with a ThreatPatternType metadata. |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
- TestParseHashThreatPatternType) { |
+TEST_F(V4GetHashProtocolManagerTest, TestParseHashThreatPatternType) { |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
base::Time now = base::Time::UnixEpoch(); |
SetTestClock(now, pm.get()); |
- // Test social engineering pattern type. |
- FindFullHashesResponse se_res; |
- se_res.mutable_negative_cache_duration()->set_seconds(600); |
- ThreatMatch* se = se_res.add_matches(); |
- se->set_threat_type(SOCIAL_ENGINEERING_PUBLIC); |
- se->set_platform_type(CHROME_PLATFORM); |
- se->set_threat_entry_type(URL); |
- SBFullHash hash_string = SBFullHashForString("Everything's shiny, Cap'n."); |
- se->mutable_threat()->set_hash(SBFullHashToString(hash_string)); |
- ThreatEntryMetadata::MetadataEntry* se_meta = |
- se->mutable_threat_entry_metadata()->add_entries(); |
- se_meta->set_key("se_pattern_type"); |
- se_meta->set_value("SOCIAL_ENGINEERING_LANDING"); |
- |
- std::string se_data; |
- se_res.SerializeToString(&se_data); |
- |
- std::vector<SBFullHashResult> full_hashes; |
- base::Time cache_expire; |
- EXPECT_TRUE(pm->ParseHashResponse(se_data, &full_hashes, &cache_expire)); |
+ { |
Nathan Parker
2016/09/09 21:26:22
I'm glad you scoped these separately, and I think
vakh (use Gerrit instead)
2016/09/09 23:25:18
I'm usually in favor of keeping tests small and fo
|
+ // Test social engineering pattern type. |
+ FindFullHashesResponse se_res; |
+ se_res.mutable_negative_cache_duration()->set_seconds(600); |
+ ThreatMatch* se = se_res.add_matches(); |
+ se->set_threat_type(SOCIAL_ENGINEERING_PUBLIC); |
+ se->set_platform_type(CHROME_PLATFORM); |
+ se->set_threat_entry_type(URL); |
+ FullHash full_hash("Everything's shiny, Cap'n."); |
+ se->mutable_threat()->set_hash(full_hash); |
+ ThreatEntryMetadata::MetadataEntry* se_meta = |
+ se->mutable_threat_entry_metadata()->add_entries(); |
+ se_meta->set_key("se_pattern_type"); |
+ se_meta->set_value("SOCIAL_ENGINEERING_LANDING"); |
+ |
+ std::string se_data; |
+ se_res.SerializeToString(&se_data); |
+ |
+ std::vector<FullHashInfo> full_hash_infos; |
+ base::Time cache_expire; |
+ EXPECT_TRUE( |
+ pm->ParseHashResponse(se_data, &full_hash_infos, &cache_expire)); |
+ EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
+ |
+ ASSERT_EQ(1ul, full_hash_infos.size()); |
+ const FullHashInfo& fhi = full_hash_infos[0]; |
+ EXPECT_EQ(full_hash, fhi.full_hash); |
+ const UpdateListIdentifier list_id(CHROME_PLATFORM, URL, |
+ SOCIAL_ENGINEERING_PUBLIC); |
+ EXPECT_EQ(list_id, fhi.list_id); |
+ EXPECT_EQ(ThreatPatternType::SOCIAL_ENGINEERING_LANDING, |
+ fhi.metadata.threat_pattern_type); |
+ } |
- EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
- EXPECT_EQ(1ul, full_hashes.size()); |
- EXPECT_TRUE(SBFullHashEqual(hash_string, full_hashes[0].hash)); |
- EXPECT_EQ(ThreatPatternType::SOCIAL_ENGINEERING_LANDING, |
- full_hashes[0].metadata.threat_pattern_type); |
- |
- // Test potentially harmful application pattern type. |
- FindFullHashesResponse pha_res; |
- pha_res.mutable_negative_cache_duration()->set_seconds(600); |
- ThreatMatch* pha = pha_res.add_matches(); |
- pha->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION); |
- pha->set_threat_entry_type(URL); |
- pha->set_platform_type(CHROME_PLATFORM); |
- hash_string = SBFullHashForString("Not to fret."); |
- pha->mutable_threat()->set_hash(SBFullHashToString(hash_string)); |
- ThreatEntryMetadata::MetadataEntry* pha_meta = |
- pha->mutable_threat_entry_metadata()->add_entries(); |
- pha_meta->set_key("pha_pattern_type"); |
- pha_meta->set_value("LANDING"); |
- |
- std::string pha_data; |
- pha_res.SerializeToString(&pha_data); |
- full_hashes.clear(); |
- EXPECT_TRUE(pm->ParseHashResponse(pha_data, &full_hashes, &cache_expire)); |
- EXPECT_EQ(1ul, full_hashes.size()); |
- EXPECT_TRUE(SBFullHashEqual(hash_string, full_hashes[0].hash)); |
- EXPECT_EQ(ThreatPatternType::MALWARE_LANDING, |
- full_hashes[0].metadata.threat_pattern_type); |
- |
- // Test invalid pattern type. |
- FindFullHashesResponse invalid_res; |
- invalid_res.mutable_negative_cache_duration()->set_seconds(600); |
- ThreatMatch* invalid = invalid_res.add_matches(); |
- invalid->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION); |
- invalid->set_threat_entry_type(URL); |
- invalid->set_platform_type(CHROME_PLATFORM); |
- invalid->mutable_threat()->set_hash(SBFullHashToString(hash_string)); |
- ThreatEntryMetadata::MetadataEntry* invalid_meta = |
- invalid->mutable_threat_entry_metadata()->add_entries(); |
- invalid_meta->set_key("pha_pattern_type"); |
- invalid_meta->set_value("INVALIDE_VALUE"); |
- |
- std::string invalid_data; |
- invalid_res.SerializeToString(&invalid_data); |
- full_hashes.clear(); |
- EXPECT_FALSE( |
- pm->ParseHashResponse(invalid_data, &full_hashes, &cache_expire)); |
- EXPECT_EQ(0ul, full_hashes.size()); |
+ { |
+ // Test potentially harmful application pattern type. |
+ FindFullHashesResponse pha_res; |
+ pha_res.mutable_negative_cache_duration()->set_seconds(600); |
+ ThreatMatch* pha = pha_res.add_matches(); |
+ pha->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION); |
+ pha->set_threat_entry_type(URL); |
+ pha->set_platform_type(CHROME_PLATFORM); |
+ FullHash full_hash("Not to fret."); |
+ pha->mutable_threat()->set_hash(full_hash); |
+ ThreatEntryMetadata::MetadataEntry* pha_meta = |
+ pha->mutable_threat_entry_metadata()->add_entries(); |
+ pha_meta->set_key("pha_pattern_type"); |
+ pha_meta->set_value("LANDING"); |
+ |
+ std::string pha_data; |
+ pha_res.SerializeToString(&pha_data); |
+ std::vector<FullHashInfo> full_hash_infos; |
+ base::Time cache_expire; |
+ EXPECT_TRUE( |
+ pm->ParseHashResponse(pha_data, &full_hash_infos, &cache_expire)); |
+ EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
+ |
+ ASSERT_EQ(1ul, full_hash_infos.size()); |
+ const FullHashInfo& fhi = full_hash_infos[0]; |
+ EXPECT_EQ(full_hash, fhi.full_hash); |
+ const UpdateListIdentifier list_id(CHROME_PLATFORM, URL, |
+ POTENTIALLY_HARMFUL_APPLICATION); |
+ EXPECT_EQ(list_id, fhi.list_id); |
+ EXPECT_EQ(ThreatPatternType::MALWARE_LANDING, |
+ fhi.metadata.threat_pattern_type); |
+ } |
+ |
+ { |
+ // Test invalid pattern type. |
+ FullHash full_hash("Not to fret."); |
+ FindFullHashesResponse invalid_res; |
+ invalid_res.mutable_negative_cache_duration()->set_seconds(600); |
+ ThreatMatch* invalid = invalid_res.add_matches(); |
+ invalid->set_threat_type(POTENTIALLY_HARMFUL_APPLICATION); |
+ invalid->set_threat_entry_type(URL); |
+ invalid->set_platform_type(CHROME_PLATFORM); |
+ invalid->mutable_threat()->set_hash(full_hash); |
+ ThreatEntryMetadata::MetadataEntry* invalid_meta = |
+ invalid->mutable_threat_entry_metadata()->add_entries(); |
+ invalid_meta->set_key("pha_pattern_type"); |
+ invalid_meta->set_value("INVALIDE_VALUE"); |
+ |
+ std::string invalid_data; |
+ invalid_res.SerializeToString(&invalid_data); |
+ std::vector<FullHashInfo> full_hash_infos; |
+ base::Time cache_expire; |
+ EXPECT_FALSE( |
+ pm->ParseHashResponse(invalid_data, &full_hash_infos, &cache_expire)); |
+ EXPECT_EQ(0ul, full_hash_infos.size()); |
+ } |
} |
// Adds metadata with a key value that is not "permission". |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
+TEST_F(V4GetHashProtocolManagerTest, |
TestParseHashResponseNonPermissionMetadata) { |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
@@ -370,8 +487,7 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
m->set_threat_type(API_ABUSE); |
m->set_platform_type(CHROME_PLATFORM); |
m->set_threat_entry_type(URL); |
- m->mutable_threat()->set_hash( |
- SBFullHashToString(SBFullHashForString("Not to fret."))); |
+ m->mutable_threat()->set_hash(FullHash("Not to fret.")); |
ThreatEntryMetadata::MetadataEntry* e = |
m->mutable_threat_entry_metadata()->add_entries(); |
e->set_key("notpermission"); |
@@ -381,15 +497,16 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
std::string res_data; |
res.SerializeToString(&res_data); |
- std::vector<SBFullHashResult> full_hashes; |
+ std::vector<FullHashInfo> full_hash_infos; |
base::Time cache_expire; |
- EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_expire)); |
+ EXPECT_FALSE( |
+ pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire)); |
EXPECT_EQ(now + base::TimeDelta::FromSeconds(600), cache_expire); |
- EXPECT_EQ(0ul, full_hashes.size()); |
+ EXPECT_EQ(0ul, full_hash_infos.size()); |
} |
-TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
+TEST_F(V4GetHashProtocolManagerTest, |
TestParseHashResponseInconsistentThreatTypes) { |
std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
@@ -399,22 +516,194 @@ TEST_F(SafeBrowsingV4GetHashProtocolManagerTest, |
m1->set_threat_type(API_ABUSE); |
m1->set_platform_type(CHROME_PLATFORM); |
m1->set_threat_entry_type(URL); |
- m1->mutable_threat()->set_hash( |
- SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); |
+ m1->mutable_threat()->set_hash(FullHash("Everything's shiny, Cap'n.")); |
m1->mutable_threat_entry_metadata()->add_entries(); |
ThreatMatch* m2 = res.add_matches(); |
m2->set_threat_type(MALWARE_THREAT); |
m2->set_threat_entry_type(URL); |
- m2->mutable_threat()->set_hash( |
- SBFullHashToString(SBFullHashForString("Not to fret."))); |
+ m2->mutable_threat()->set_hash(FullHash("Not to fret.")); |
// Serialize. |
std::string res_data; |
res.SerializeToString(&res_data); |
- std::vector<SBFullHashResult> full_hashes; |
+ std::vector<FullHashInfo> full_hash_infos; |
base::Time cache_expire; |
- EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_expire)); |
+ EXPECT_FALSE( |
+ pm->ParseHashResponse(res_data, &full_hash_infos, &cache_expire)); |
+} |
+ |
+// Checks that results are looked up correctly in the cache. |
+TEST_F(V4GetHashProtocolManagerTest, GetCachedResults) { |
Nathan Parker
2016/09/09 21:26:22
An overall comment: I think it would be less fragi
|
+ base::Time now = base::Time::UnixEpoch(); |
+ FullHash full_hash("example.com/"); |
Nathan Parker
2016/09/09 21:26:22
nit: A url-as-hash is sortof confusing
vakh (use Gerrit instead)
2016/09/09 23:25:18
Done.
|
+ HashPrefix prefix("exam"); |
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; |
+ std::vector<HashPrefix> prefixes_to_request; |
+ std::vector<FullHashInfo> cached_full_hash_infos; |
+ full_hash_to_store_and_hash_prefixes[full_hash].push_back( |
+ StoreAndHashPrefix(GetUrlMalwareId(), prefix)); |
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
+ FullHashCache* cache = pm->full_hash_cache_for_tests(); |
+ |
+ // Test with an empty cache. (Case: 2) |
+ pm->GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, now, |
+ &prefixes_to_request, &cached_full_hash_infos); |
+ EXPECT_TRUE(cache->empty()); |
+ ASSERT_EQ(1ul, prefixes_to_request.size()); |
+ EXPECT_EQ(prefix, prefixes_to_request[0]); |
+ EXPECT_TRUE(cached_full_hash_infos.empty()); |
+ |
+ // Prefix has a cache entry but full hash is not there. (Case: 1-b-i) |
+ CachedHashPrefixInfo* entry = &(*cache)[prefix]; |
Nathan Parker
2016/09/09 21:26:21
Do all these cases need to be in one test? Could y
vakh (use Gerrit instead)
2016/09/09 23:25:18
Scoped each of the cases, and clear the cache befo
|
+ entry->negative_ttl = now + base::TimeDelta::FromMinutes(5); |
+ prefixes_to_request.clear(); |
+ cached_full_hash_infos.clear(); |
+ pm->GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, now, |
+ &prefixes_to_request, &cached_full_hash_infos); |
+ EXPECT_TRUE(prefixes_to_request.empty()); |
+ EXPECT_TRUE(cached_full_hash_infos.empty()); |
+ |
+ // Expired negative cache entry. (Case: 1-b-ii) |
+ entry->negative_ttl = now - base::TimeDelta::FromMinutes(5); |
+ prefixes_to_request.clear(); |
+ cached_full_hash_infos.clear(); |
+ pm->GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, now, |
+ &prefixes_to_request, &cached_full_hash_infos); |
+ ASSERT_EQ(1ul, prefixes_to_request.size()); |
+ EXPECT_EQ(prefix, prefixes_to_request[0]); |
+ EXPECT_TRUE(cached_full_hash_infos.empty()); |
+ |
+ // Now put unexpired full hash in the cache. (Case: 1-a-i) |
+ FullHashInfo fhi(full_hash, GetUrlMalwareId(), |
+ now + base::TimeDelta::FromMinutes(3)); |
+ entry->full_hash_infos.push_back(fhi); |
+ prefixes_to_request.clear(); |
+ cached_full_hash_infos.clear(); |
+ pm->GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, now, |
+ &prefixes_to_request, &cached_full_hash_infos); |
+ EXPECT_TRUE(prefixes_to_request.empty()); |
+ ASSERT_EQ(1ul, cached_full_hash_infos.size()); |
+ EXPECT_EQ(full_hash, cached_full_hash_infos[0].full_hash); |
+ |
+ // Expire the full hash in the cache. (Case: 1-a-ii) |
+ entry->full_hash_infos[0].positive_ttl = |
+ now - base::TimeDelta::FromMinutes(3); |
+ prefixes_to_request.clear(); |
+ cached_full_hash_infos.clear(); |
+ pm->GetFullHashCachedResults(full_hash_to_store_and_hash_prefixes, now, |
+ &prefixes_to_request, &cached_full_hash_infos); |
+ ASSERT_EQ(1ul, prefixes_to_request.size()); |
+ EXPECT_EQ(prefix, prefixes_to_request[0]); |
+ EXPECT_TRUE(cached_full_hash_infos.empty()); |
+} |
+ |
+TEST_F(V4GetHashProtocolManagerTest, TestUpdatesAreMerged) { |
+ // We'll add one of the requested FullHashInfo objects into the cache, and |
+ // inject the other one as a response from the server. The result should |
+ // include both FullHashInfo objects. |
+ |
+ net::TestURLFetcherFactory factory; |
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
+ HashPrefix prefix_1("exam"); |
+ FullHash full_hash_1("example.com/test"); |
Nathan Parker
2016/09/09 21:26:22
nit: url-as-hash is confusing
vakh (use Gerrit instead)
2016/09/09 23:25:18
Done.
|
+ HashPrefix prefix_2("Everything"); |
+ FullHash full_hash_2("Everything's shiny, Cap'n."); |
+ |
+ base::Time now = Time::Now(); |
+ SetTestClock(now, pm.get()); |
+ |
+ FullHashCache* cache = pm->full_hash_cache_for_tests(); |
+ CachedHashPrefixInfo* entry = &(*cache)[prefix_1]; |
+ entry->negative_ttl = now + base::TimeDelta::FromMinutes(100); |
+ // Put one unexpired full hash in the cache for a store we'll look in. |
+ entry->full_hash_infos.emplace_back(full_hash_1, GetUrlMalwareId(), |
+ now + base::TimeDelta::FromSeconds(200)); |
+ // Put one unexpired full hash in the cache for a store we'll not look in. |
+ entry->full_hash_infos.emplace_back(full_hash_1, GetUrlSocEngId(), |
+ now + base::TimeDelta::FromSeconds(200)); |
+ |
+ // Request full hash information from two stores. |
+ FullHashToStoreAndHashPrefixesMap full_hash_to_store_and_hash_prefixes; |
+ full_hash_to_store_and_hash_prefixes[full_hash_1].push_back( |
+ StoreAndHashPrefix(GetUrlMalwareId(), prefix_1)); |
+ full_hash_to_store_and_hash_prefixes[full_hash_2].push_back( |
+ StoreAndHashPrefix(GetChromeUrlApiId(), prefix_2)); |
+ |
+ // Expect full hash information from both stores. |
+ std::vector<FullHashInfo> expected_results; |
+ expected_results.emplace_back(full_hash_1, GetUrlMalwareId(), |
+ now + base::TimeDelta::FromSeconds(200)); |
+ expected_results.emplace_back(full_hash_2, GetChromeUrlApiId(), |
+ now + base::TimeDelta::FromSeconds(300)); |
+ expected_results[1].metadata.api_permissions.insert("NOTIFICATIONS"); |
+ |
+ pm->GetFullHashes( |
+ full_hash_to_store_and_hash_prefixes, |
+ base::Bind(&V4GetHashProtocolManagerTest::ValidateGetV4HashResults, |
+ base::Unretained(this), expected_results)); |
+ |
+ SetupFetcherToReturnOKResponse(factory, GetStockV4HashResponseInfos()); |
+ |
+ // No error, back off multiplier is unchanged. |
+ EXPECT_EQ(0ul, pm->gethash_error_count_); |
+ EXPECT_EQ(1ul, pm->gethash_back_off_mult_); |
+ |
+ // Verify the state of the cache. |
+ ASSERT_EQ(2u, cache->size()); |
+ const CachedHashPrefixInfo& cached_result_1 = cache->at(prefix_1); |
+ EXPECT_EQ(cached_result_1.negative_ttl, |
+ now + base::TimeDelta::FromMinutes(100)); |
+ ASSERT_EQ(2u, cached_result_1.full_hash_infos.size()); |
+ EXPECT_EQ(full_hash_1, cached_result_1.full_hash_infos[0].full_hash); |
+ EXPECT_EQ(GetUrlMalwareId(), cached_result_1.full_hash_infos[0].list_id); |
+ |
+ const CachedHashPrefixInfo& cached_result_2 = cache->at(prefix_2); |
+ EXPECT_EQ(cached_result_2.negative_ttl, |
+ now + base::TimeDelta::FromSeconds(600)); |
+ ASSERT_EQ(1u, cached_result_2.full_hash_infos.size()); |
+ EXPECT_EQ(full_hash_2, cached_result_2.full_hash_infos[0].full_hash); |
+ EXPECT_EQ(GetChromeUrlApiId(), cached_result_2.full_hash_infos[0].list_id); |
+ EXPECT_TRUE(callback_called()); |
+} |
+ |
+// The server responds back with full hash information containing metadata |
+// information for one of the full hashes for the URL in test. |
+TEST_F(V4GetHashProtocolManagerTest, TestGetFullHashesWithApisMergesMetadata) { |
+ net::TestURLFetcherFactory factory; |
+ const GURL url("https://www.example.com/more"); |
+ ThreatMetadata expected_md; |
+ expected_md.api_permissions.insert("NOTIFICATIONS"); |
+ expected_md.api_permissions.insert("AUDIO_CAPTURE"); |
+ std::unique_ptr<V4GetHashProtocolManager> pm(CreateProtocolManager()); |
+ pm->GetFullHashesWithApis( |
+ url, base::Bind(&V4GetHashProtocolManagerTest::ValidateGetV4ApiResults, |
+ base::Unretained(this), expected_md)); |
+ |
+ // The following two random looking strings value are two of the full hashes |
+ // produced by UrlToFullHashes in v4_protocol_manager_util.h for the URL: |
+ // "https://www.example.com/more" |
+ std::vector<ResponseInfo> infos; |
+ FullHash full_hash; |
+ base::Base64Decode("1ZzJ0/7NjPkg6t0DAS8L5Jf7jA48Pn7opQcP4UXYeXc=", |
+ &full_hash); |
+ ResponseInfo info(full_hash, GetChromeUrlApiId()); |
+ info.key_values.emplace_back("permission", "NOTIFICATIONS"); |
+ infos.push_back(info); |
+ |
+ base::Base64Decode("4rPDSdcei1BiCOPnj9kgsy2O6Ua6X3iFBakqphB3ZfA=", |
+ &full_hash); |
+ info = ResponseInfo(full_hash, GetChromeUrlApiId()); |
+ info.key_values.emplace_back("permission", "AUDIO_CAPTURE"); |
+ infos.push_back(info); |
+ |
+ full_hash = FullHash("Everything's shiny, Cap'n."); |
+ info = ResponseInfo(full_hash, GetChromeUrlApiId()); |
+ info.key_values.emplace_back("permission", "GEOLOCATION"); |
+ infos.push_back(info); |
+ SetupFetcherToReturnOKResponse(factory, infos); |
+ |
+ EXPECT_TRUE(callback_called()); |
} |
} // namespace safe_browsing |