| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 #include "components/safe_browsing/password_protection/password_protection_servi
ce.h" | 4 #include "components/safe_browsing/password_protection/password_protection_servi
ce.h" |
| 5 | 5 |
| 6 #include "base/memory/ptr_util.h" | 6 #include "base/memory/ptr_util.h" |
| 7 #include "base/run_loop.h" | 7 #include "base/run_loop.h" |
| 8 #include "base/strings/string_number_conversions.h" |
| 8 #include "base/test/histogram_tester.h" | 9 #include "base/test/histogram_tester.h" |
| 9 #include "components/safe_browsing_db/test_database_manager.h" | 10 #include "components/safe_browsing_db/test_database_manager.h" |
| 11 #include "components/sync_preferences/testing_pref_service_syncable.h" |
| 10 #include "content/public/test/test_browser_thread_bundle.h" | 12 #include "content/public/test/test_browser_thread_bundle.h" |
| 11 #include "testing/gmock/include/gmock/gmock.h" | 13 #include "testing/gmock/include/gmock/gmock.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
| 13 | 15 |
| 14 namespace { | 16 namespace { |
| 15 | 17 |
| 16 const char kPasswordReuseMatchWhitelistHistogramName[] = | 18 const char kPasswordReuseMatchWhitelistHistogramName[] = |
| 17 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist"; | 19 "PasswordManager.PasswordReuse.MainFrameMatchCsdWhitelist"; |
| 18 const char kWhitelistedUrl[] = "http://inwhitelist.com"; | 20 const char kWhitelistedUrl[] = "http://inwhitelist.com"; |
| 19 const char kNoneWhitelistedUrl[] = "http://notinwhitelist.com"; | 21 const char kNoneWhitelistedUrl[] = "http://notinwhitelist.com"; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 32 ~MockSafeBrowsingDatabaseManager() override {} | 34 ~MockSafeBrowsingDatabaseManager() override {} |
| 33 | 35 |
| 34 private: | 36 private: |
| 35 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); | 37 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); |
| 36 }; | 38 }; |
| 37 | 39 |
| 38 class PasswordProtectionServiceTest : public testing::Test { | 40 class PasswordProtectionServiceTest : public testing::Test { |
| 39 public: | 41 public: |
| 40 PasswordProtectionServiceTest(){}; | 42 PasswordProtectionServiceTest(){}; |
| 41 | 43 |
| 44 ~PasswordProtectionServiceTest() override { |
| 45 content_setting_map_->ShutdownOnUIThread(); |
| 46 } |
| 47 |
| 48 LoginReputationClientResponse CreateVerdictProto( |
| 49 LoginReputationClientResponse::VerdictType verdict, |
| 50 int cache_duration_sec, |
| 51 const std::string& cache_expression) { |
| 52 LoginReputationClientResponse verdict_proto; |
| 53 verdict_proto.set_verdict_type(verdict); |
| 54 verdict_proto.set_cache_duration_sec(cache_duration_sec); |
| 55 verdict_proto.set_cache_expression(cache_expression); |
| 56 return verdict_proto; |
| 57 } |
| 58 |
| 42 void SetUp() override { | 59 void SetUp() override { |
| 43 database_manager_ = new MockSafeBrowsingDatabaseManager(); | 60 database_manager_ = new MockSafeBrowsingDatabaseManager(); |
| 44 password_protection_service_ = | 61 password_protection_service_ = |
| 45 base::MakeUnique<PasswordProtectionService>(database_manager_); | 62 base::MakeUnique<PasswordProtectionService>(database_manager_); |
| 63 HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry()); |
| 64 content_setting_map_ = new HostContentSettingsMap( |
| 65 &test_pref_service_, false /* incognito */, false /* guest_profile */); |
| 66 } |
| 67 |
| 68 bool UrlMatchCacheExpression(const GURL& url, |
| 69 const std::string& cache_expression) { |
| 70 std::vector<std::string> hosts; |
| 71 std::vector<std::string> paths; |
| 72 V4ProtocolManagerUtil::GenerateHostAndPathVariants(url, &hosts, &paths); |
| 73 return PasswordProtectionService::HostPathVariantsMatchCacheExpression( |
| 74 hosts, paths, cache_expression); |
| 46 } | 75 } |
| 47 | 76 |
| 48 protected: | 77 protected: |
| 49 // |thread_bundle_| is needed here because this test involves both UI and IO | 78 // |thread_bundle_| is needed here because this test involves both UI and IO |
| 50 // threads. | 79 // threads. |
| 51 content::TestBrowserThreadBundle thread_bundle_; | 80 content::TestBrowserThreadBundle thread_bundle_; |
| 52 scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_; | 81 scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_; |
| 53 std::unique_ptr<PasswordProtectionService> password_protection_service_; | 82 std::unique_ptr<PasswordProtectionService> password_protection_service_; |
| 83 sync_preferences::TestingPrefServiceSyncable test_pref_service_; |
| 84 scoped_refptr<HostContentSettingsMap> content_setting_map_; |
| 54 }; | 85 }; |
| 55 | 86 |
| 56 TEST_F(PasswordProtectionServiceTest, | 87 TEST_F(PasswordProtectionServiceTest, |
| 57 TestPasswordReuseMatchWhitelistHistogram) { | 88 TestPasswordReuseMatchWhitelistHistogram) { |
| 58 const GURL whitelisted_url(kWhitelistedUrl); | 89 const GURL whitelisted_url(kWhitelistedUrl); |
| 59 const GURL not_whitelisted_url(kNoneWhitelistedUrl); | 90 const GURL not_whitelisted_url(kNoneWhitelistedUrl); |
| 60 EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(whitelisted_url)) | 91 EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(whitelisted_url)) |
| 61 .WillOnce(testing::Return(true)); | 92 .WillOnce(testing::Return(true)); |
| 62 EXPECT_CALL(*database_manager_.get(), | 93 EXPECT_CALL(*database_manager_.get(), |
| 63 MatchCsdWhitelistUrl(not_whitelisted_url)) | 94 MatchCsdWhitelistUrl(not_whitelisted_url)) |
| (...skipping 14 matching lines...) Expand all Loading... |
| 78 testing::ElementsAre(base::Bucket(1, 1))); | 109 testing::ElementsAre(base::Bucket(1, 1))); |
| 79 | 110 |
| 80 // Non-whitelisted url should increase "False" bucket by 1. | 111 // Non-whitelisted url should increase "False" bucket by 1. |
| 81 password_protection_service_->RecordPasswordReuse(not_whitelisted_url); | 112 password_protection_service_->RecordPasswordReuse(not_whitelisted_url); |
| 82 base::RunLoop().RunUntilIdle(); | 113 base::RunLoop().RunUntilIdle(); |
| 83 EXPECT_THAT( | 114 EXPECT_THAT( |
| 84 histograms.GetAllSamples(kPasswordReuseMatchWhitelistHistogramName), | 115 histograms.GetAllSamples(kPasswordReuseMatchWhitelistHistogramName), |
| 85 testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); | 116 testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); |
| 86 } | 117 } |
| 87 | 118 |
| 119 TEST_F(PasswordProtectionServiceTest, TestParseVerdictEntry) { |
| 120 int now_in_sec = static_cast<int>(base::Time::Now().ToDoubleT()); |
| 121 std::unique_ptr<base::DictionaryValue> valid_verdict_entry = |
| 122 base::MakeUnique<base::DictionaryValue>(); |
| 123 valid_verdict_entry->SetInteger("cache_ttl", 10 * 60); |
| 124 valid_verdict_entry->SetInteger("cache_creation_time", now_in_sec); |
| 125 valid_verdict_entry->SetInteger("verdict", 1 /* SAFE */); |
| 126 |
| 127 std::unique_ptr<base::DictionaryValue> invalid_verdict_entry = |
| 128 base::MakeUnique<base::DictionaryValue>(); |
| 129 invalid_verdict_entry->SetString("cache_ttl", "invalid_ttl"); |
| 130 |
| 131 int cache_creation_time, cache_ttl, verdict_type_value; |
| 132 // ParseVerdictEntry fails if input is empty. |
| 133 EXPECT_FALSE(PasswordProtectionService::ParseVerdictEntry( |
| 134 nullptr, &cache_creation_time, &cache_ttl, &verdict_type_value)); |
| 135 |
| 136 // ParseVerdictEntry fails if the input dict value is invalid. |
| 137 EXPECT_FALSE(PasswordProtectionService::ParseVerdictEntry( |
| 138 invalid_verdict_entry.get(), &cache_creation_time, &cache_ttl, |
| 139 &verdict_type_value)); |
| 140 |
| 141 // ParseVerdictEntry success case. |
| 142 ASSERT_TRUE(PasswordProtectionService::ParseVerdictEntry( |
| 143 valid_verdict_entry.get(), &cache_creation_time, &cache_ttl, |
| 144 &verdict_type_value)); |
| 145 EXPECT_EQ(600, cache_ttl); |
| 146 EXPECT_EQ(now_in_sec, cache_creation_time); |
| 147 EXPECT_EQ(LoginReputationClientResponse::SAFE, |
| 148 static_cast<LoginReputationClientResponse::VerdictType>( |
| 149 verdict_type_value)); |
| 150 } |
| 151 |
| 152 TEST_F(PasswordProtectionServiceTest, TestUrlMatchCacheExpression) { |
| 153 // Cache expression without path. |
| 154 std::string cache_expression("google.com"); |
| 155 EXPECT_TRUE(UrlMatchCacheExpression(GURL("https://www.google.com"), |
| 156 cache_expression)); |
| 157 EXPECT_TRUE( |
| 158 UrlMatchCacheExpression(GURL("http://google.com"), cache_expression)); |
| 159 EXPECT_TRUE(UrlMatchCacheExpression(GURL("https://maps.google.com"), |
| 160 cache_expression)); |
| 161 EXPECT_TRUE(UrlMatchCacheExpression( |
| 162 GURL("https://www.google.com/maps/local/"), cache_expression)); |
| 163 |
| 164 // Cache expression with sub-domain. |
| 165 cache_expression = "maps.google.com"; |
| 166 EXPECT_FALSE( |
| 167 UrlMatchCacheExpression(GURL("https://google.com"), cache_expression)); |
| 168 EXPECT_FALSE(UrlMatchCacheExpression(GURL("https://www.google.com"), |
| 169 cache_expression)); |
| 170 EXPECT_TRUE(UrlMatchCacheExpression(GURL("https://maps.google.com"), |
| 171 cache_expression)); |
| 172 EXPECT_TRUE(UrlMatchCacheExpression(GURL("https://maps.google.com/local/"), |
| 173 cache_expression)); |
| 174 |
| 175 // Cache expression with path. |
| 176 cache_expression = std::string("evil.com/bad"); |
| 177 EXPECT_TRUE(UrlMatchCacheExpression(GURL("http://evil.com/bad/index.html"), |
| 178 cache_expression)); |
| 179 EXPECT_FALSE(UrlMatchCacheExpression(GURL("http://evil.com/worse/index.html"), |
| 180 cache_expression)); |
| 181 EXPECT_TRUE(UrlMatchCacheExpression( |
| 182 GURL("http://phishing.malware.evil.com/bad/index.html"), |
| 183 cache_expression)); |
| 184 } |
| 185 |
| 186 TEST_F(PasswordProtectionServiceTest, TestSetGetAndClearCachedVerdict) { |
| 187 // Assume each verdict has a TTL of 10 minutes. |
| 188 LoginReputationClientResponse verdict_proto_1(CreateVerdictProto( |
| 189 LoginReputationClientResponse::SAFE, 10 * 60, "test.com/foo")); |
| 190 LoginReputationClientResponse verdict_proto_2(CreateVerdictProto( |
| 191 LoginReputationClientResponse::LOW_REPUTATION, 10 * 60, "test.com/bar")); |
| 192 LoginReputationClientResponse verdict_proto_3(CreateVerdictProto( |
| 193 LoginReputationClientResponse::PHISHING, 10 * 60, "evil.com")); |
| 194 LoginReputationClientResponse verdict_proto_4(CreateVerdictProto( |
| 195 LoginReputationClientResponse::PHISHING, 10 * 60, "test.com")); |
| 196 |
| 197 base::Time now = base::Time::Now(); |
| 198 base::Time yesterday = |
| 199 base::Time::FromDoubleT(now.ToDoubleT() - 24.0 * 60.0 * 60.0); |
| 200 base::Time one_minute_ago = base::Time::FromDoubleT(now.ToDoubleT() - 60.0); |
| 201 password_protection_service_->CacheVerdict( |
| 202 GURL("http://www.test.com/foo/index.html"), &verdict_proto_1, now, |
| 203 content_setting_map_.get()); |
| 204 password_protection_service_->CacheVerdict( |
| 205 GURL("http://www.test.com/bar/index.html"), &verdict_proto_2, yesterday, |
| 206 content_setting_map_.get()); |
| 207 password_protection_service_->CacheVerdict( |
| 208 GURL("http://phishing.evil.com/local/login.html"), &verdict_proto_3, |
| 209 one_minute_ago, content_setting_map_.get()); |
| 210 password_protection_service_->CacheVerdict( |
| 211 GURL("http://www.test.com/index.html"), &verdict_proto_4, now, |
| 212 content_setting_map_.get()); |
| 213 |
| 214 // Return VERDICT_TYPE_UNSPECIFIED if content setting map has nothing of this |
| 215 // origin. |
| 216 EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED, |
| 217 password_protection_service_->GetCachedVerdict( |
| 218 content_setting_map_.get(), |
| 219 GURL("http://www.unkown.com/index.html"))); |
| 220 |
| 221 // Return VERDICT_TYPE_UNSPECIFIED if verdict is expired. |
| 222 EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED, |
| 223 password_protection_service_->GetCachedVerdict( |
| 224 content_setting_map_.get(), |
| 225 GURL("http://www.test.com/bar/test.html"))); |
| 226 |
| 227 // Return the verdict type associated with the most specific cache expression |
| 228 // if verdict is available and not expired. |
| 229 EXPECT_EQ(LoginReputationClientResponse::SAFE, |
| 230 password_protection_service_->GetCachedVerdict( |
| 231 content_setting_map_.get(), |
| 232 GURL("http://www.test.com/foo/index1.html"))); |
| 233 EXPECT_EQ(LoginReputationClientResponse::PHISHING, |
| 234 password_protection_service_->GetCachedVerdict( |
| 235 content_setting_map_.get(), |
| 236 GURL("http://phishing.evil.com/phishing/"))); |
| 237 |
| 238 // Cache www.test.com/bar again, but this time it is not expired. |
| 239 password_protection_service_->CacheVerdict( |
| 240 GURL("http://www.test.com/bar/index.html"), &verdict_proto_2, now, |
| 241 content_setting_map_.get()); |
| 242 EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION, |
| 243 password_protection_service_->GetCachedVerdict( |
| 244 content_setting_map_.get(), |
| 245 GURL("http://www.test.com/bar/test2.html"))); |
| 246 |
| 247 // Now, there should be cached verdicts for 2 origins: http://www.test.com, |
| 248 // and http://phishing.evil.com |
| 249 ContentSettingsForOneType password_protection_settings; |
| 250 content_setting_map_->GetSettingsForOneType( |
| 251 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), |
| 252 &password_protection_settings); |
| 253 EXPECT_EQ(2U, password_protection_settings.size()); |
| 254 |
| 255 history::URLRows deleted_urls; |
| 256 deleted_urls.push_back( |
| 257 history::URLRow(GURL("http://phishing.evil.com/phishing/"))); |
| 258 // We delete the history of http://phishing.evil.com/phishing. |
| 259 password_protection_service_->RemoveContentSettingsOnURLsDeleted( |
| 260 false, deleted_urls, content_setting_map_.get()); |
| 261 content_setting_map_->GetSettingsForOneType( |
| 262 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), |
| 263 &password_protection_settings); |
| 264 EXPECT_EQ(1U, password_protection_settings.size()); |
| 265 EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION, |
| 266 password_protection_service_->GetCachedVerdict( |
| 267 content_setting_map_.get(), |
| 268 GURL("http://www.test.com/bar/test2.html"))); |
| 269 |
| 270 // Now we delete all history. All password protection content settings will be |
| 271 // gone too. |
| 272 password_protection_service_->RemoveContentSettingsOnURLsDeleted( |
| 273 true, deleted_urls, content_setting_map_.get()); |
| 274 content_setting_map_->GetSettingsForOneType( |
| 275 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), |
| 276 &password_protection_settings); |
| 277 EXPECT_EQ(0U, password_protection_settings.size()); |
| 278 } |
| 279 |
| 88 } // namespace safe_browsing | 280 } // namespace safe_browsing |
| OLD | NEW |