Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: components/safe_browsing/password_protection/password_protection_service.cc

Issue 2911293003: Reland: Cache protected password entry and password on focus ping separately. (Closed)
Patch Set: nit Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
lpz 2017/06/08 15:18:33 meta comment: this might be just my ignorance, but
Jialiu Lin 2017/06/08 20:47:30 Yes, UMFAMILIAR_LOGIN_PAGE(a.k.a password on focus
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 4
5 #include "components/safe_browsing/password_protection/password_protection_servi ce.h" 5 #include "components/safe_browsing/password_protection/password_protection_servi ce.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "base/callback.h" 9 #include "base/callback.h"
10 #include "base/memory/ptr_util.h" 10 #include "base/memory/ptr_util.h"
11 #include "base/metrics/field_trial.h" 11 #include "base/metrics/field_trial.h"
(...skipping 19 matching lines...) Expand all
31 namespace safe_browsing { 31 namespace safe_browsing {
32 32
33 namespace { 33 namespace {
34 34
35 // Keys for storing password protection verdict into a DictionaryValue. 35 // Keys for storing password protection verdict into a DictionaryValue.
36 const char kCacheCreationTime[] = "cache_creation_time"; 36 const char kCacheCreationTime[] = "cache_creation_time";
37 const char kVerdictProto[] = "verdict_proto"; 37 const char kVerdictProto[] = "verdict_proto";
38 const int kRequestTimeoutMs = 10000; 38 const int kRequestTimeoutMs = 10000;
39 const char kPasswordProtectionRequestUrl[] = 39 const char kPasswordProtectionRequestUrl[] =
40 "https://sb-ssl.google.com/safebrowsing/clientreport/login"; 40 "https://sb-ssl.google.com/safebrowsing/clientreport/login";
41 const char kPasswordOnFocusCacheKey[] = "password_on_focus_cache_key";
41 42
42 // Helper function to determine if the given origin matches content settings 43 // Helper function to determine if the given origin matches content settings
43 // map's patterns. 44 // map's patterns.
44 bool OriginMatchPrimaryPattern( 45 bool OriginMatchPrimaryPattern(
45 const GURL& origin, 46 const GURL& origin,
46 const ContentSettingsPattern& primary_pattern, 47 const ContentSettingsPattern& primary_pattern,
47 const ContentSettingsPattern& secondary_pattern_unused) { 48 const ContentSettingsPattern& secondary_pattern_unused) {
48 return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern; 49 return ContentSettingsPattern::FromURLNoWildcard(origin) == primary_pattern;
49 } 50 }
50 51
(...skipping 29 matching lines...) Expand all
80 const char kPasswordOnFocusRequestOutcomeHistogramName[] = 81 const char kPasswordOnFocusRequestOutcomeHistogramName[] =
81 "PasswordProtection.RequestOutcome.PasswordFieldOnFocus"; 82 "PasswordProtection.RequestOutcome.PasswordFieldOnFocus";
82 const char kPasswordEntryRequestOutcomeHistogramName[] = 83 const char kPasswordEntryRequestOutcomeHistogramName[] =
83 "PasswordProtection.RequestOutcome.ProtectedPasswordEntry"; 84 "PasswordProtection.RequestOutcome.ProtectedPasswordEntry";
84 85
85 PasswordProtectionService::PasswordProtectionService( 86 PasswordProtectionService::PasswordProtectionService(
86 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager, 87 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
87 scoped_refptr<net::URLRequestContextGetter> request_context_getter, 88 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
88 HistoryService* history_service, 89 HistoryService* history_service,
89 HostContentSettingsMap* host_content_settings_map) 90 HostContentSettingsMap* host_content_settings_map)
90 : stored_verdict_count_(-1), 91 : stored_verdict_count_password_on_focus_(-1),
92 stored_verdict_count_password_entry_(-1),
91 database_manager_(database_manager), 93 database_manager_(database_manager),
92 request_context_getter_(request_context_getter), 94 request_context_getter_(request_context_getter),
93 history_service_observer_(this), 95 history_service_observer_(this),
94 content_settings_(host_content_settings_map), 96 content_settings_(host_content_settings_map),
95 weak_factory_(this) { 97 weak_factory_(this) {
96 DCHECK_CURRENTLY_ON(BrowserThread::UI); 98 DCHECK_CURRENTLY_ON(BrowserThread::UI);
97 if (history_service) 99 if (history_service)
98 history_service_observer_.Add(history_service); 100 history_service_observer_.Add(history_service);
99 } 101 }
100 102
(...skipping 17 matching lines...) Expand all
118 return false; 120 return false;
119 121
120 const std::string& hostname = url.HostNoBrackets(); 122 const std::string& hostname = url.HostNoBrackets();
121 return !net::IsLocalhost(hostname) && !net::IsHostnameNonUnique(hostname) && 123 return !net::IsLocalhost(hostname) && !net::IsHostnameNonUnique(hostname) &&
122 hostname.find('.') != std::string::npos; 124 hostname.find('.') != std::string::npos;
123 } 125 }
124 126
125 LoginReputationClientResponse::VerdictType 127 LoginReputationClientResponse::VerdictType
126 PasswordProtectionService::GetCachedVerdict( 128 PasswordProtectionService::GetCachedVerdict(
127 const GURL& url, 129 const GURL& url,
130 TriggerType type,
128 LoginReputationClientResponse* out_response) { 131 LoginReputationClientResponse* out_response) {
132 DCHECK(content_settings_);
133 DCHECK(type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
134 type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
135
129 if (!url.is_valid()) 136 if (!url.is_valid())
130 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 137 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
131 138
132 DCHECK(content_settings_);
133
134 GURL hostname = GetHostNameWithHTTPScheme(url); 139 GURL hostname = GetHostNameWithHTTPScheme(url);
135 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 140 std::unique_ptr<base::DictionaryValue> cache_dictionary =
136 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 141 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
137 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 142 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
138 std::string(), nullptr)); 143 std::string(), nullptr));
139 // Return early if there is no verdict cached for this origin. 144
140 if (!verdict_dictionary.get() || verdict_dictionary->empty()) 145 if (!cache_dictionary.get() || cache_dictionary->empty())
141 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 146 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
142 147
148 base::DictionaryValue* verdict_dictionary = nullptr;
149 if (type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
150 // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
151 // are cached under |kPasswordOnFocusCacheKey|.
152 if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
153 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
154 }
155 DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
156 kPasswordOnFocusCacheKey, &verdict_dictionary));
157 } else {
158 verdict_dictionary = cache_dictionary.get();
159 }
160
143 std::vector<std::string> paths; 161 std::vector<std::string> paths;
144 GeneratePathVariantsWithoutQuery(url, &paths); 162 GeneratePathVariantsWithoutQuery(url, &paths);
145 int max_path_depth = -1; 163 int max_path_depth = -1;
146 LoginReputationClientResponse::VerdictType most_matching_verdict = 164 LoginReputationClientResponse::VerdictType most_matching_verdict =
147 LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 165 LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
148 // For all the verdicts of the same origin, we key them by |cache_expression|. 166 // For all the verdicts of the same origin, we key them by |cache_expression|.
149 // Its corresponding value is a DictionaryValue contains its creation time and 167 // Its corresponding value is a DictionaryValue contains its creation time and
150 // the serialized verdict proto. 168 // the serialized verdict proto.
151 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get()); 169 for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
152 !it.IsAtEnd(); it.Advance()) { 170 it.Advance()) {
171 if (type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
172 it.key() == kPasswordOnFocusCacheKey) {
173 continue;
174 }
153 base::DictionaryValue* verdict_entry = nullptr; 175 base::DictionaryValue* verdict_entry = nullptr;
154 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion( 176 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
155 it.key() /* cache_expression */, &verdict_entry)); 177 it.key() /* cache_expression */, &verdict_entry));
156 int verdict_received_time; 178 int verdict_received_time;
157 LoginReputationClientResponse verdict; 179 LoginReputationClientResponse verdict;
158 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict)); 180 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
159 // Since password protection content settings are keyed by origin, we only 181 // Since password protection content settings are keyed by origin, we only
160 // need to compare the path part of the cache_expression and the given url. 182 // need to compare the path part of the cache_expression and the given url.
161 std::string cache_expression_path = 183 std::string cache_expression_path =
162 GetCacheExpressionPath(verdict.cache_expression()); 184 GetCacheExpressionPath(verdict.cache_expression());
(...skipping 10 matching lines...) Expand all
173 ? LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED 195 ? LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED
174 : verdict.verdict_type(); 196 : verdict.verdict_type();
175 out_response->CopyFrom(verdict); 197 out_response->CopyFrom(verdict);
176 } 198 }
177 } 199 }
178 return most_matching_verdict; 200 return most_matching_verdict;
179 } 201 }
180 202
181 void PasswordProtectionService::CacheVerdict( 203 void PasswordProtectionService::CacheVerdict(
182 const GURL& url, 204 const GURL& url,
205 TriggerType type,
183 LoginReputationClientResponse* verdict, 206 LoginReputationClientResponse* verdict,
184 const base::Time& receive_time) { 207 const base::Time& receive_time) {
185 DCHECK(verdict); 208 DCHECK(verdict);
186 DCHECK(content_settings_); 209 DCHECK(content_settings_);
210 DCHECK(type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
211 type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
187 212
188 GURL hostname = GetHostNameWithHTTPScheme(url); 213 GURL hostname = GetHostNameWithHTTPScheme(url);
189 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 214 int* stored_verdict_count =
215 type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
216 ? &stored_verdict_count_password_on_focus_
217 : &stored_verdict_count_password_entry_;
218 std::unique_ptr<base::DictionaryValue> cache_dictionary =
190 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 219 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
191 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 220 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
192 std::string(), nullptr)); 221 std::string(), nullptr));
193 222
194 if (!verdict_dictionary.get()) 223 if (!cache_dictionary.get())
195 verdict_dictionary = base::MakeUnique<base::DictionaryValue>(); 224 cache_dictionary = base::MakeUnique<base::DictionaryValue>();
196 225
197 std::unique_ptr<base::DictionaryValue> verdict_entry = 226 std::unique_ptr<base::DictionaryValue> verdict_entry =
198 CreateDictionaryFromVerdict(verdict, receive_time); 227 CreateDictionaryFromVerdict(verdict, receive_time);
199 228
229 base::DictionaryValue* verdict_dictionary = nullptr;
230 if (type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
231 // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
232 // are cached under |kPasswordOnFocusCacheKey|.
233 if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
234 cache_dictionary->SetDictionaryWithoutPathExpansion(
235 kPasswordOnFocusCacheKey, base::MakeUnique<base::DictionaryValue>());
236 }
237 DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
238 kPasswordOnFocusCacheKey, &verdict_dictionary));
239 } else {
240 verdict_dictionary = cache_dictionary.get();
241 }
242
200 // Increases stored verdict count if we haven't seen this cache expression 243 // Increases stored verdict count if we haven't seen this cache expression
201 // before. 244 // before.
202 if (!verdict_dictionary->HasKey(verdict->cache_expression())) 245 if (!verdict_dictionary->HasKey(verdict->cache_expression()))
203 stored_verdict_count_ = GetStoredVerdictCount() + 1; 246 *stored_verdict_count = GetStoredVerdictCount(type) + 1;
204 // If same cache_expression is already in this verdict_dictionary, we simply 247 // If same cache_expression is already in this verdict_dictionary, we simply
205 // override it. 248 // override it.
206 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(), 249 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
207 std::move(verdict_entry)); 250 std::move(verdict_entry));
208 content_settings_->SetWebsiteSettingDefaultScope( 251 content_settings_->SetWebsiteSettingDefaultScope(
209 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 252 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
210 std::string(), std::move(verdict_dictionary)); 253 std::string(), std::move(cache_dictionary));
211 } 254 }
212 255
213 void PasswordProtectionService::CleanUpExpiredVerdicts() { 256 void PasswordProtectionService::CleanUpExpiredVerdicts() {
214 DCHECK(content_settings_); 257 DCHECK(content_settings_);
215 if (GetStoredVerdictCount() <= 0) 258
259 if (GetStoredVerdictCount(
260 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
261 GetStoredVerdictCount(
262 LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
216 return; 263 return;
217 264
218 ContentSettingsForOneType password_protection_settings; 265 ContentSettingsForOneType password_protection_settings;
219 content_settings_->GetSettingsForOneType( 266 content_settings_->GetSettingsForOneType(
220 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 267 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
221 &password_protection_settings); 268 &password_protection_settings);
222 269
223 for (const ContentSettingPatternSource& source : 270 for (const ContentSettingPatternSource& source :
224 password_protection_settings) { 271 password_protection_settings) {
225 GURL primary_pattern_url = GURL(source.primary_pattern.ToString()); 272 GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
226 // Find all verdicts associated with this origin. 273 // Find all verdicts associated with this origin.
227 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 274 std::unique_ptr<base::DictionaryValue> cache_dictionary =
228 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 275 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
229 primary_pattern_url, GURL(), 276 primary_pattern_url, GURL(),
230 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr)); 277 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
231 std::vector<std::string> expired_keys; 278 bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
232 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get()); 279 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
233 !it.IsAtEnd(); it.Advance()) { 280 cache_dictionary.get());
234 base::DictionaryValue* verdict_entry = nullptr; 281 bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
235 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion( 282 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
236 it.key(), &verdict_entry)); 283 cache_dictionary.get());
237 int verdict_received_time;
238 LoginReputationClientResponse verdict;
239 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
240 284
241 if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) { 285 if (cache_dictionary->size() == 0u) {
242 // Since DictionaryValue::Iterator cannot be used to modify the
243 // dictionary, we record the keys of expired verdicts in |expired_keys|
244 // and remove them in the next for-loop.
245 expired_keys.push_back(it.key());
246 }
247 }
248
249 for (const std::string& key : expired_keys) {
250 verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
251 stored_verdict_count_--;
252 }
253
254 if (verdict_dictionary->size() == 0u) {
255 content_settings_->ClearSettingsForOneTypeWithPredicate( 286 content_settings_->ClearSettingsForOneTypeWithPredicate(
256 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(), 287 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
257 base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url)); 288 base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
258 } else if (expired_keys.size() > 0u) { 289 } else if (has_expired_password_on_focus_entry ||
290 has_expired_password_reuse_entry) {
259 // Set the website setting of this origin with the updated 291 // Set the website setting of this origin with the updated
260 // |verdict_diectionary|. 292 // |verdict_diectionary|.
261 content_settings_->SetWebsiteSettingDefaultScope( 293 content_settings_->SetWebsiteSettingDefaultScope(
262 primary_pattern_url, GURL(), 294 primary_pattern_url, GURL(),
263 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 295 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
264 std::move(verdict_dictionary)); 296 std::move(cache_dictionary));
265 } 297 }
266 } 298 }
267 } 299 }
268 300
269 void PasswordProtectionService::StartRequest( 301 void PasswordProtectionService::StartRequest(
270 WebContents* web_contents, 302 WebContents* web_contents,
271 const GURL& main_frame_url, 303 const GURL& main_frame_url,
272 const GURL& password_form_action, 304 const GURL& password_form_action,
273 const GURL& password_form_frame_url, 305 const GURL& password_form_frame_url,
274 const std::string& saved_domain, 306 const std::string& saved_domain,
275 LoginReputationClientRequest::TriggerType type) { 307 TriggerType type) {
276 DCHECK_CURRENTLY_ON(BrowserThread::UI); 308 DCHECK_CURRENTLY_ON(BrowserThread::UI);
277 scoped_refptr<PasswordProtectionRequest> request( 309 scoped_refptr<PasswordProtectionRequest> request(
278 new PasswordProtectionRequest(web_contents, main_frame_url, 310 new PasswordProtectionRequest(web_contents, main_frame_url,
279 password_form_action, 311 password_form_action,
280 password_form_frame_url, saved_domain, type, 312 password_form_frame_url, saved_domain, type,
281 this, GetRequestTimeoutInMS())); 313 this, GetRequestTimeoutInMS()));
282 DCHECK(request); 314 DCHECK(request);
283 request->Start(); 315 request->Start();
284 requests_.insert(std::move(request)); 316 requests_.insert(std::move(request));
285 } 317 }
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 354
323 void PasswordProtectionService::RequestFinished( 355 void PasswordProtectionService::RequestFinished(
324 PasswordProtectionRequest* request, 356 PasswordProtectionRequest* request,
325 bool already_cached, 357 bool already_cached,
326 std::unique_ptr<LoginReputationClientResponse> response) { 358 std::unique_ptr<LoginReputationClientResponse> response) {
327 DCHECK_CURRENTLY_ON(BrowserThread::UI); 359 DCHECK_CURRENTLY_ON(BrowserThread::UI);
328 DCHECK(request); 360 DCHECK(request);
329 361
330 if (response) { 362 if (response) {
331 if (!already_cached) { 363 if (!already_cached) {
332 CacheVerdict(request->main_frame_url(), response.get(), 364 CacheVerdict(request->main_frame_url(), request->request_type(),
333 base::Time::Now()); 365 response.get(), base::Time::Now());
334 } 366 }
335 367
336 if (request->request_type() == 368 if (request->request_type() ==
337 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE && 369 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
338 response->verdict_type() == LoginReputationClientResponse::PHISHING && 370 response->verdict_type() == LoginReputationClientResponse::PHISHING &&
339 base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) { 371 base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
340 ShowPhishingInterstitial(request->main_frame_url(), 372 ShowPhishingInterstitial(request->main_frame_url(),
341 response->verdict_token(), 373 response->verdict_token(),
342 request->web_contents()); 374 request->web_contents());
343 } 375 }
(...skipping 25 matching lines...) Expand all
369 return database_manager_; 401 return database_manager_;
370 } 402 }
371 403
372 GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() { 404 GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
373 GURL url(kPasswordProtectionRequestUrl); 405 GURL url(kPasswordProtectionRequestUrl);
374 std::string api_key = google_apis::GetAPIKey(); 406 std::string api_key = google_apis::GetAPIKey();
375 DCHECK(!api_key.empty()); 407 DCHECK(!api_key.empty());
376 return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true)); 408 return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
377 } 409 }
378 410
379 int PasswordProtectionService::GetStoredVerdictCount() { 411 int PasswordProtectionService::GetStoredVerdictCount(TriggerType type) {
380 DCHECK(content_settings_); 412 DCHECK(content_settings_);
413 DCHECK(type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
414 type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
415 int* stored_verdict_count =
416 type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
417 ? &stored_verdict_count_password_on_focus_
418 : &stored_verdict_count_password_entry_;
381 // If we have already computed this, return its value. 419 // If we have already computed this, return its value.
382 if (stored_verdict_count_ >= 0) 420 if (*stored_verdict_count >= 0)
383 return stored_verdict_count_; 421 return *stored_verdict_count;
384 422
385 ContentSettingsForOneType password_protection_settings; 423 ContentSettingsForOneType password_protection_settings;
386 content_settings_->GetSettingsForOneType( 424 content_settings_->GetSettingsForOneType(
387 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 425 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
388 &password_protection_settings); 426 &password_protection_settings);
389 stored_verdict_count_ = 0; 427 stored_verdict_count_password_on_focus_ = 0;
428 stored_verdict_count_password_entry_ = 0;
390 if (password_protection_settings.empty()) 429 if (password_protection_settings.empty())
391 return 0; 430 return 0;
392 431
393 for (const ContentSettingPatternSource& source : 432 for (const ContentSettingPatternSource& source :
394 password_protection_settings) { 433 password_protection_settings) {
395 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 434 std::unique_ptr<base::DictionaryValue> verdict_dictionary =
lpz 2017/06/08 15:18:33 should this be "cache_dictionary" for consistency
Jialiu Lin 2017/06/08 20:47:30 Done.
396 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 435 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
397 GURL(source.primary_pattern.ToString()), GURL(), 436 GURL(source.primary_pattern.ToString()), GURL(),
398 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr)); 437 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
399 if (verdict_dictionary.get() && !verdict_dictionary->empty()) 438 if (verdict_dictionary.get() && !verdict_dictionary->empty()) {
lpz 2017/06/08 15:18:33 I think this block could use a couple comments (ie
Jialiu Lin 2017/06/08 20:47:30 The outer dict size is # of PASSWORD_REUSE_EVENT v
400 stored_verdict_count_ += static_cast<int>(verdict_dictionary->size()); 439 stored_verdict_count_password_entry_ +=
440 static_cast<int>(verdict_dictionary->size());
441 if (verdict_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
442 stored_verdict_count_password_entry_ -= 1;
443 base::DictionaryValue* password_on_focus_dict = nullptr;
444 verdict_dictionary->GetDictionaryWithoutPathExpansion(
445 kPasswordOnFocusCacheKey, &password_on_focus_dict);
446 stored_verdict_count_password_on_focus_ +=
447 static_cast<int>(password_on_focus_dict->size());
448 }
449 }
401 } 450 }
402 return stored_verdict_count_; 451 return *stored_verdict_count;
403 } 452 }
404 453
405 int PasswordProtectionService::GetRequestTimeoutInMS() { 454 int PasswordProtectionService::GetRequestTimeoutInMS() {
406 return kRequestTimeoutMs; 455 return kRequestTimeoutMs;
407 } 456 }
408 457
409 void PasswordProtectionService::FillUserPopulation( 458 void PasswordProtectionService::FillUserPopulation(
410 const LoginReputationClientRequest::TriggerType& request_type, 459 TriggerType request_type,
411 LoginReputationClientRequest* request_proto) { 460 LoginReputationClientRequest* request_proto) {
412 ChromeUserPopulation* user_population = request_proto->mutable_population(); 461 ChromeUserPopulation* user_population = request_proto->mutable_population();
413 user_population->set_user_population( 462 user_population->set_user_population(
414 IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING 463 IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
415 : ChromeUserPopulation::SAFE_BROWSING); 464 : ChromeUserPopulation::SAFE_BROWSING);
416 user_population->set_is_history_sync_enabled(IsHistorySyncEnabled()); 465 user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
417 466
418 base::FieldTrial* low_reputation_field_trial = 467 base::FieldTrial* low_reputation_field_trial =
419 base::FeatureList::GetFieldTrial(kPasswordFieldOnFocusPinging); 468 base::FeatureList::GetFieldTrial(kPasswordFieldOnFocusPinging);
420 if (low_reputation_field_trial) { 469 if (low_reputation_field_trial) {
421 user_population->add_finch_active_groups( 470 user_population->add_finch_active_groups(
422 low_reputation_field_trial->trial_name() + "|" + 471 low_reputation_field_trial->trial_name() + "|" +
423 low_reputation_field_trial->group_name()); 472 low_reputation_field_trial->group_name());
424 } 473 }
425 base::FieldTrial* password_entry_field_trial = 474 base::FieldTrial* password_entry_field_trial =
426 base::FeatureList::GetFieldTrial(kProtectedPasswordEntryPinging); 475 base::FeatureList::GetFieldTrial(kProtectedPasswordEntryPinging);
427 if (password_entry_field_trial) { 476 if (password_entry_field_trial) {
428 user_population->add_finch_active_groups( 477 user_population->add_finch_active_groups(
429 low_reputation_field_trial->trial_name() + "|" + 478 low_reputation_field_trial->trial_name() + "|" +
430 low_reputation_field_trial->group_name()); 479 low_reputation_field_trial->group_name());
431 } 480 }
432 } 481 }
433 482
434 void PasswordProtectionService::OnURLsDeleted( 483 void PasswordProtectionService::OnURLsDeleted(
435 history::HistoryService* history_service, 484 history::HistoryService* history_service,
436 bool all_history, 485 bool all_history,
437 bool expired, 486 bool expired,
438 const history::URLRows& deleted_rows, 487 const history::URLRows& deleted_rows,
439 const std::set<GURL>& favicon_urls) { 488 const std::set<GURL>& favicon_urls) {
440 if (stored_verdict_count_ <= 0) 489 if (stored_verdict_count_password_on_focus_ <= 0 &&
490 stored_verdict_count_password_entry_ <= 0) {
441 return; 491 return;
492 }
442 493
443 BrowserThread::PostTask( 494 BrowserThread::PostTask(
444 BrowserThread::UI, FROM_HERE, 495 BrowserThread::UI, FROM_HERE,
445 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted, 496 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
446 GetWeakPtr(), all_history, deleted_rows)); 497 GetWeakPtr(), all_history, deleted_rows));
447 } 498 }
448 499
449 void PasswordProtectionService::HistoryServiceBeingDeleted( 500 void PasswordProtectionService::HistoryServiceBeingDeleted(
450 history::HistoryService* history_service) { 501 history::HistoryService* history_service) {
451 history_service_observer_.RemoveAll(); 502 history_service_observer_.RemoveAll();
452 } 503 }
453 504
454 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted( 505 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
455 bool all_history, 506 bool all_history,
456 const history::URLRows& deleted_rows) { 507 const history::URLRows& deleted_rows) {
457 DCHECK_CURRENTLY_ON(BrowserThread::UI); 508 DCHECK_CURRENTLY_ON(BrowserThread::UI);
458 DCHECK(content_settings_); 509 DCHECK(content_settings_);
459 510
460 if (all_history) { 511 if (all_history) {
461 content_settings_->ClearSettingsForOneType( 512 content_settings_->ClearSettingsForOneType(
462 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION); 513 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
463 stored_verdict_count_ = 0; 514 stored_verdict_count_password_on_focus_ = 0;
515 stored_verdict_count_password_entry_ = 0;
464 return; 516 return;
465 } 517 }
466 518
467 // For now, if a URL is deleted from history, we simply remove all the 519 // For now, if a URL is deleted from history, we simply remove all the
468 // cached verdicts of the same origin. This is a pretty aggressive deletion. 520 // cached verdicts of the same origin. This is a pretty aggressive deletion.
469 // We might revisit this logic later to decide if we want to only delete the 521 // We might revisit this logic later to decide if we want to only delete the
470 // cached verdict whose cache expression matches this URL. 522 // cached verdict whose cache expression matches this URL.
471 for (const history::URLRow& row : deleted_rows) { 523 for (const history::URLRow& row : deleted_rows) {
472 if (!row.url().SchemeIsHTTPOrHTTPS()) 524 if (!row.url().SchemeIsHTTPOrHTTPS())
473 continue; 525 continue;
474 GURL url_key = GetHostNameWithHTTPScheme(row.url()); 526 GURL url_key = GetHostNameWithHTTPScheme(row.url());
475 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 527 stored_verdict_count_password_on_focus_ =
476 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 528 GetStoredVerdictCount(
477 url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 529 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
478 std::string(), nullptr)); 530 GetVerdictCountForURL(
479 531 url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
480 // Move on if we have no cached verdict for this deleted history row. 532 stored_verdict_count_password_entry_ =
481 if (!verdict_dictionary.get() || verdict_dictionary->empty()) 533 GetStoredVerdictCount(
482 continue; 534 LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
483 535 GetVerdictCountForURL(
484 int verdict_count = static_cast<int>(verdict_dictionary->size()); 536 url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
485 stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
486 content_settings_->ClearSettingsForOneTypeWithPredicate( 537 content_settings_->ClearSettingsForOneTypeWithPredicate(
487 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(), 538 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
488 base::Bind(&OriginMatchPrimaryPattern, url_key)); 539 base::Bind(&OriginMatchPrimaryPattern, url_key));
489 } 540 }
490 } 541 }
491 542
543 int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
544 TriggerType type) {
545 DCHECK(type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
546 type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
547 std::unique_ptr<base::DictionaryValue> verdict_dictionary =
548 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
549 url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
550 nullptr));
551 if (!verdict_dictionary.get() || verdict_dictionary->empty())
552 return 0;
553
554 if (type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
555 base::DictionaryValue* password_on_focus_dict = nullptr;
556 verdict_dictionary->GetDictionaryWithoutPathExpansion(
557 kPasswordOnFocusCacheKey, &password_on_focus_dict);
558 return password_on_focus_dict ? password_on_focus_dict->size() : 0;
559 } else {
560 return verdict_dictionary->HasKey(kPasswordOnFocusCacheKey)
561 ? verdict_dictionary->size() - 1
562 : verdict_dictionary->size();
563 }
564 }
565
566 bool PasswordProtectionService::RemoveExpiredVerdicts(
567 TriggerType type,
568 base::DictionaryValue* cache_dict) {
569 DCHECK(type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
570 type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
571 base::DictionaryValue* verdict_dictionary = nullptr;
572 if (type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
573 verdict_dictionary = cache_dict;
574 } else {
575 if (!cache_dict->GetDictionaryWithoutPathExpansion(kPasswordOnFocusCacheKey,
576 &verdict_dictionary)) {
577 return false;
578 }
579 }
580
581 if (!verdict_dictionary || verdict_dictionary->empty())
582 return false;
lpz 2017/06/08 15:18:33 nit: +1 newline after
Jialiu Lin 2017/06/08 20:47:30 Done.
583 std::vector<std::string> expired_keys;
584 for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
585 it.Advance()) {
586 if (type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
587 it.key() == std::string(kPasswordOnFocusCacheKey))
588 continue;
lpz 2017/06/08 15:18:33 nit: +1 newline after
Jialiu Lin 2017/06/08 20:47:30 Done.
589 base::DictionaryValue* verdict_entry = nullptr;
590 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
591 it.key(), &verdict_entry));
592 int verdict_received_time;
593 LoginReputationClientResponse verdict;
594 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
595
596 if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
597 // Since DictionaryValue::Iterator cannot be used to modify the
598 // dictionary, we record the keys of expired verdicts in |expired_keys|
599 // and remove them in the next for-loop.
600 expired_keys.push_back(it.key());
601 }
602 }
603
604 for (const std::string& key : expired_keys) {
605 verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
606 if (type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
607 stored_verdict_count_password_entry_--;
608 else
609 stored_verdict_count_password_on_focus_--;
610 }
611
612 if (type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
613 verdict_dictionary->size() == 0U) {
614 cache_dict->RemoveWithoutPathExpansion(kPasswordOnFocusCacheKey, nullptr);
615 }
616
617 return expired_keys.size() > 0U;
618 }
619
492 // static 620 // static
493 bool PasswordProtectionService::ParseVerdictEntry( 621 bool PasswordProtectionService::ParseVerdictEntry(
494 base::DictionaryValue* verdict_entry, 622 base::DictionaryValue* verdict_entry,
495 int* out_verdict_received_time, 623 int* out_verdict_received_time,
496 LoginReputationClientResponse* out_verdict) { 624 LoginReputationClientResponse* out_verdict) {
497 std::string serialized_verdict_proto; 625 std::string serialized_verdict_proto;
498 return verdict_entry && out_verdict && 626 return verdict_entry && out_verdict &&
499 verdict_entry->GetInteger(kCacheCreationTime, 627 verdict_entry->GetInteger(kCacheCreationTime,
500 out_verdict_received_time) && 628 out_verdict_received_time) &&
501 verdict_entry->GetString(kVerdictProto, &serialized_verdict_proto) && 629 verdict_entry->GetString(kVerdictProto, &serialized_verdict_proto) &&
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 if (is_password_entry_ping) { 710 if (is_password_entry_ping) {
583 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason, 711 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason,
584 MAX_OUTCOME); 712 MAX_OUTCOME);
585 } else { 713 } else {
586 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName, 714 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
587 reason, MAX_OUTCOME); 715 reason, MAX_OUTCOME);
588 } 716 }
589 } 717 }
590 718
591 } // namespace safe_browsing 719 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698