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

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: rebase update 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.
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"
(...skipping 20 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 14 matching lines...) Expand all
115 117
116 bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) { 118 bool PasswordProtectionService::CanGetReputationOfURL(const GURL& url) {
117 if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) 119 if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS())
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
127 // We cache both types of pings under the same content settings type (
128 // CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION). Since UNFAMILIAR_LOGING_PAGE
129 // verdicts are only enabled on extended reporting users, we cache them one
130 // layer lower in the content setting DictionaryValue than PASSWORD_REUSE_EVENT
131 // verdicts.
132 // In other words, to cache a UNFAMILIAR_LOGIN_PAGE verdict we needs two levels
133 // of keys: (1) origin, (2) cache expression returned in verdict.
134 // To cache a PASSWORD_REUSE_EVENT, three levels of keys are used:
135 // (1) origin, (2) 2nd level key is always |kPasswordOnFocusCacheKey|,
136 // (3) cache expression.
125 LoginReputationClientResponse::VerdictType 137 LoginReputationClientResponse::VerdictType
126 PasswordProtectionService::GetCachedVerdict( 138 PasswordProtectionService::GetCachedVerdict(
127 const GURL& url, 139 const GURL& url,
140 TriggerType trigger_type,
128 LoginReputationClientResponse* out_response) { 141 LoginReputationClientResponse* out_response) {
142 DCHECK(content_settings_);
vakh (use Gerrit instead) 2017/06/09 23:28:09 This DCHECK is not useful since the de-reference b
Jialiu Lin 2017/06/09 23:33:41 Good catch. Removed this dcheck.
143 DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
144 trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
145
129 if (!url.is_valid()) 146 if (!url.is_valid())
130 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 147 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
131 148
132 DCHECK(content_settings_);
133
134 GURL hostname = GetHostNameWithHTTPScheme(url); 149 GURL hostname = GetHostNameWithHTTPScheme(url);
135 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 150 std::unique_ptr<base::DictionaryValue> cache_dictionary =
136 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 151 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
137 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 152 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
138 std::string(), nullptr)); 153 std::string(), nullptr));
139 // Return early if there is no verdict cached for this origin. 154
140 if (!verdict_dictionary.get() || verdict_dictionary->empty()) 155 if (!cache_dictionary.get() || cache_dictionary->empty())
141 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 156 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
142 157
158 base::DictionaryValue* verdict_dictionary = nullptr;
159 if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
160 // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
161 // are cached under |kPasswordOnFocusCacheKey|.
162 if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
163 return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
164 }
165 DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
166 kPasswordOnFocusCacheKey, &verdict_dictionary));
167 } else {
168 verdict_dictionary = cache_dictionary.get();
169 }
170
143 std::vector<std::string> paths; 171 std::vector<std::string> paths;
144 GeneratePathVariantsWithoutQuery(url, &paths); 172 GeneratePathVariantsWithoutQuery(url, &paths);
145 int max_path_depth = -1; 173 int max_path_depth = -1;
146 LoginReputationClientResponse::VerdictType most_matching_verdict = 174 LoginReputationClientResponse::VerdictType most_matching_verdict =
147 LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED; 175 LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
148 // For all the verdicts of the same origin, we key them by |cache_expression|. 176 // 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 177 // Its corresponding value is a DictionaryValue contains its creation time and
150 // the serialized verdict proto. 178 // the serialized verdict proto.
151 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get()); 179 for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
152 !it.IsAtEnd(); it.Advance()) { 180 it.Advance()) {
181 if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
182 it.key() == kPasswordOnFocusCacheKey) {
183 continue;
184 }
153 base::DictionaryValue* verdict_entry = nullptr; 185 base::DictionaryValue* verdict_entry = nullptr;
154 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion( 186 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
155 it.key() /* cache_expression */, &verdict_entry)); 187 it.key() /* cache_expression */, &verdict_entry));
156 int verdict_received_time; 188 int verdict_received_time;
157 LoginReputationClientResponse verdict; 189 LoginReputationClientResponse verdict;
158 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict)); 190 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
159 // Since password protection content settings are keyed by origin, we only 191 // 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. 192 // need to compare the path part of the cache_expression and the given url.
161 std::string cache_expression_path = 193 std::string cache_expression_path =
162 GetCacheExpressionPath(verdict.cache_expression()); 194 GetCacheExpressionPath(verdict.cache_expression());
(...skipping 10 matching lines...) Expand all
173 ? LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED 205 ? LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED
174 : verdict.verdict_type(); 206 : verdict.verdict_type();
175 out_response->CopyFrom(verdict); 207 out_response->CopyFrom(verdict);
176 } 208 }
177 } 209 }
178 return most_matching_verdict; 210 return most_matching_verdict;
179 } 211 }
180 212
181 void PasswordProtectionService::CacheVerdict( 213 void PasswordProtectionService::CacheVerdict(
182 const GURL& url, 214 const GURL& url,
215 TriggerType trigger_type,
183 LoginReputationClientResponse* verdict, 216 LoginReputationClientResponse* verdict,
184 const base::Time& receive_time) { 217 const base::Time& receive_time) {
185 DCHECK(verdict); 218 DCHECK(verdict);
186 DCHECK(content_settings_); 219 DCHECK(content_settings_);
220 DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
221 trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
187 222
188 GURL hostname = GetHostNameWithHTTPScheme(url); 223 GURL hostname = GetHostNameWithHTTPScheme(url);
189 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 224 int* stored_verdict_count =
225 trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
226 ? &stored_verdict_count_password_on_focus_
227 : &stored_verdict_count_password_entry_;
228 std::unique_ptr<base::DictionaryValue> cache_dictionary =
190 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 229 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
191 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 230 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
192 std::string(), nullptr)); 231 std::string(), nullptr));
193 232
194 if (!verdict_dictionary.get()) 233 if (!cache_dictionary.get())
195 verdict_dictionary = base::MakeUnique<base::DictionaryValue>(); 234 cache_dictionary = base::MakeUnique<base::DictionaryValue>();
196 235
197 std::unique_ptr<base::DictionaryValue> verdict_entry = 236 std::unique_ptr<base::DictionaryValue> verdict_entry =
198 CreateDictionaryFromVerdict(verdict, receive_time); 237 CreateDictionaryFromVerdict(verdict, receive_time);
199 238
239 base::DictionaryValue* verdict_dictionary = nullptr;
240 if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
241 // All UNFAMILIAR_LOGIN_PAGE verdicts (a.k.a password on focus ping)
242 // are cached under |kPasswordOnFocusCacheKey|.
243 if (!cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
244 cache_dictionary->SetDictionaryWithoutPathExpansion(
245 kPasswordOnFocusCacheKey, base::MakeUnique<base::DictionaryValue>());
246 }
247 DCHECK(cache_dictionary->GetDictionaryWithoutPathExpansion(
248 kPasswordOnFocusCacheKey, &verdict_dictionary));
249 } else {
250 verdict_dictionary = cache_dictionary.get();
251 }
252
200 // Increases stored verdict count if we haven't seen this cache expression 253 // Increases stored verdict count if we haven't seen this cache expression
201 // before. 254 // before.
202 if (!verdict_dictionary->HasKey(verdict->cache_expression())) 255 if (!verdict_dictionary->HasKey(verdict->cache_expression()))
203 stored_verdict_count_ = GetStoredVerdictCount() + 1; 256 *stored_verdict_count = GetStoredVerdictCount(trigger_type) + 1;
204 // If same cache_expression is already in this verdict_dictionary, we simply 257 // If same cache_expression is already in this verdict_dictionary, we simply
205 // override it. 258 // override it.
206 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(), 259 verdict_dictionary->SetWithoutPathExpansion(verdict->cache_expression(),
207 std::move(verdict_entry)); 260 std::move(verdict_entry));
208 content_settings_->SetWebsiteSettingDefaultScope( 261 content_settings_->SetWebsiteSettingDefaultScope(
209 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 262 hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
210 std::string(), std::move(verdict_dictionary)); 263 std::string(), std::move(cache_dictionary));
211 } 264 }
212 265
213 void PasswordProtectionService::CleanUpExpiredVerdicts() { 266 void PasswordProtectionService::CleanUpExpiredVerdicts() {
214 DCHECK(content_settings_); 267 DCHECK(content_settings_);
215 if (GetStoredVerdictCount() <= 0) 268
269 if (GetStoredVerdictCount(
270 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) <= 0 &&
271 GetStoredVerdictCount(
272 LoginReputationClientRequest::PASSWORD_REUSE_EVENT) <= 0)
216 return; 273 return;
217 274
218 ContentSettingsForOneType password_protection_settings; 275 ContentSettingsForOneType password_protection_settings;
219 content_settings_->GetSettingsForOneType( 276 content_settings_->GetSettingsForOneType(
220 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 277 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
221 &password_protection_settings); 278 &password_protection_settings);
222 279
223 for (const ContentSettingPatternSource& source : 280 for (const ContentSettingPatternSource& source :
224 password_protection_settings) { 281 password_protection_settings) {
225 GURL primary_pattern_url = GURL(source.primary_pattern.ToString()); 282 GURL primary_pattern_url = GURL(source.primary_pattern.ToString());
226 // Find all verdicts associated with this origin. 283 // Find all verdicts associated with this origin.
227 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 284 std::unique_ptr<base::DictionaryValue> cache_dictionary =
228 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 285 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
229 primary_pattern_url, GURL(), 286 primary_pattern_url, GURL(),
230 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr)); 287 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
231 std::vector<std::string> expired_keys; 288 bool has_expired_password_on_focus_entry = RemoveExpiredVerdicts(
232 for (base::DictionaryValue::Iterator it(*verdict_dictionary.get()); 289 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
233 !it.IsAtEnd(); it.Advance()) { 290 cache_dictionary.get());
234 base::DictionaryValue* verdict_entry = nullptr; 291 bool has_expired_password_reuse_entry = RemoveExpiredVerdicts(
235 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion( 292 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
236 it.key(), &verdict_entry)); 293 cache_dictionary.get());
237 int verdict_received_time;
238 LoginReputationClientResponse verdict;
239 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
240 294
241 if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) { 295 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( 296 content_settings_->ClearSettingsForOneTypeWithPredicate(
256 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(), 297 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
257 base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url)); 298 base::Bind(&OriginMatchPrimaryPattern, primary_pattern_url));
258 } else if (expired_keys.size() > 0u) { 299 } else if (has_expired_password_on_focus_entry ||
300 has_expired_password_reuse_entry) {
259 // Set the website setting of this origin with the updated 301 // Set the website setting of this origin with the updated
260 // |verdict_diectionary|. 302 // |verdict_diectionary|.
261 content_settings_->SetWebsiteSettingDefaultScope( 303 content_settings_->SetWebsiteSettingDefaultScope(
262 primary_pattern_url, GURL(), 304 primary_pattern_url, GURL(),
263 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 305 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
264 std::move(verdict_dictionary)); 306 std::move(cache_dictionary));
265 } 307 }
266 } 308 }
267 } 309 }
268 310
269 void PasswordProtectionService::StartRequest( 311 void PasswordProtectionService::StartRequest(
270 WebContents* web_contents, 312 WebContents* web_contents,
271 const GURL& main_frame_url, 313 const GURL& main_frame_url,
272 const GURL& password_form_action, 314 const GURL& password_form_action,
273 const GURL& password_form_frame_url, 315 const GURL& password_form_frame_url,
274 const std::string& saved_domain, 316 const std::string& saved_domain,
275 LoginReputationClientRequest::TriggerType type, 317 TriggerType trigger_type,
276 bool password_field_exists) { 318 bool password_field_exists) {
277 DCHECK_CURRENTLY_ON(BrowserThread::UI); 319 DCHECK_CURRENTLY_ON(BrowserThread::UI);
278 scoped_refptr<PasswordProtectionRequest> request( 320 scoped_refptr<PasswordProtectionRequest> request(
279 new PasswordProtectionRequest( 321 new PasswordProtectionRequest(
280 web_contents, main_frame_url, password_form_action, 322 web_contents, main_frame_url, password_form_action,
281 password_form_frame_url, saved_domain, type, password_field_exists, 323 password_form_frame_url, saved_domain, trigger_type,
282 this, GetRequestTimeoutInMS())); 324 password_field_exists, this, GetRequestTimeoutInMS()));
325
283 DCHECK(request); 326 DCHECK(request);
284 request->Start(); 327 request->Start();
285 requests_.insert(std::move(request)); 328 requests_.insert(std::move(request));
286 } 329 }
287 330
288 void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest( 331 void PasswordProtectionService::MaybeStartPasswordFieldOnFocusRequest(
289 WebContents* web_contents, 332 WebContents* web_contents,
290 const GURL& main_frame_url, 333 const GURL& main_frame_url,
291 const GURL& password_form_action, 334 const GURL& password_form_action,
292 const GURL& password_form_frame_url) { 335 const GURL& password_form_frame_url) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 368
326 void PasswordProtectionService::RequestFinished( 369 void PasswordProtectionService::RequestFinished(
327 PasswordProtectionRequest* request, 370 PasswordProtectionRequest* request,
328 bool already_cached, 371 bool already_cached,
329 std::unique_ptr<LoginReputationClientResponse> response) { 372 std::unique_ptr<LoginReputationClientResponse> response) {
330 DCHECK_CURRENTLY_ON(BrowserThread::UI); 373 DCHECK_CURRENTLY_ON(BrowserThread::UI);
331 DCHECK(request); 374 DCHECK(request);
332 375
333 if (response) { 376 if (response) {
334 if (!already_cached) { 377 if (!already_cached) {
335 CacheVerdict(request->main_frame_url(), response.get(), 378 CacheVerdict(request->main_frame_url(), request->trigger_type(),
336 base::Time::Now()); 379 response.get(), base::Time::Now());
337 } 380 }
338 381
339 if (request->request_type() == 382 if (request->trigger_type() ==
340 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE && 383 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
341 response->verdict_type() == LoginReputationClientResponse::PHISHING && 384 response->verdict_type() == LoginReputationClientResponse::PHISHING &&
342 base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) { 385 base::FeatureList::IsEnabled(kPasswordProtectionInterstitial)) {
343 ShowPhishingInterstitial(request->main_frame_url(), 386 ShowPhishingInterstitial(request->main_frame_url(),
344 response->verdict_token(), 387 response->verdict_token(),
345 request->web_contents()); 388 request->web_contents());
346 } 389 }
347 } 390 }
348 391
349 // Finished processing this request. Remove it from pending list. 392 // Finished processing this request. Remove it from pending list.
(...skipping 22 matching lines...) Expand all
372 return database_manager_; 415 return database_manager_;
373 } 416 }
374 417
375 GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() { 418 GURL PasswordProtectionService::GetPasswordProtectionRequestUrl() {
376 GURL url(kPasswordProtectionRequestUrl); 419 GURL url(kPasswordProtectionRequestUrl);
377 std::string api_key = google_apis::GetAPIKey(); 420 std::string api_key = google_apis::GetAPIKey();
378 DCHECK(!api_key.empty()); 421 DCHECK(!api_key.empty());
379 return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true)); 422 return url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
380 } 423 }
381 424
382 int PasswordProtectionService::GetStoredVerdictCount() { 425 int PasswordProtectionService::GetStoredVerdictCount(TriggerType trigger_type) {
383 DCHECK(content_settings_); 426 DCHECK(content_settings_);
427 DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
428 trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
429 int* stored_verdict_count =
430 trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE
431 ? &stored_verdict_count_password_on_focus_
432 : &stored_verdict_count_password_entry_;
384 // If we have already computed this, return its value. 433 // If we have already computed this, return its value.
385 if (stored_verdict_count_ >= 0) 434 if (*stored_verdict_count >= 0)
386 return stored_verdict_count_; 435 return *stored_verdict_count;
387 436
388 ContentSettingsForOneType password_protection_settings; 437 ContentSettingsForOneType password_protection_settings;
389 content_settings_->GetSettingsForOneType( 438 content_settings_->GetSettingsForOneType(
390 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), 439 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
391 &password_protection_settings); 440 &password_protection_settings);
392 stored_verdict_count_ = 0; 441 stored_verdict_count_password_on_focus_ = 0;
442 stored_verdict_count_password_entry_ = 0;
393 if (password_protection_settings.empty()) 443 if (password_protection_settings.empty())
394 return 0; 444 return 0;
395 445
396 for (const ContentSettingPatternSource& source : 446 for (const ContentSettingPatternSource& source :
397 password_protection_settings) { 447 password_protection_settings) {
398 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 448 std::unique_ptr<base::DictionaryValue> cache_dictionary =
399 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 449 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
400 GURL(source.primary_pattern.ToString()), GURL(), 450 GURL(source.primary_pattern.ToString()), GURL(),
401 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr)); 451 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(), nullptr));
402 if (verdict_dictionary.get() && !verdict_dictionary->empty()) 452 if (cache_dictionary.get() && !cache_dictionary->empty()) {
403 stored_verdict_count_ += static_cast<int>(verdict_dictionary->size()); 453 stored_verdict_count_password_entry_ +=
454 static_cast<int>(cache_dictionary->size());
455 if (cache_dictionary->HasKey(kPasswordOnFocusCacheKey)) {
456 // Substracts 1 from password_entry count if |kPasswordOnFocusCacheKey|
457 // presents.
458 stored_verdict_count_password_entry_ -= 1;
459 base::DictionaryValue* password_on_focus_dict = nullptr;
460 cache_dictionary->GetDictionaryWithoutPathExpansion(
461 kPasswordOnFocusCacheKey, &password_on_focus_dict);
462 stored_verdict_count_password_on_focus_ +=
463 static_cast<int>(password_on_focus_dict->size());
464 }
465 }
404 } 466 }
405 return stored_verdict_count_; 467 return *stored_verdict_count;
406 } 468 }
407 469
408 int PasswordProtectionService::GetRequestTimeoutInMS() { 470 int PasswordProtectionService::GetRequestTimeoutInMS() {
409 return kRequestTimeoutMs; 471 return kRequestTimeoutMs;
410 } 472 }
411 473
412 void PasswordProtectionService::FillUserPopulation( 474 void PasswordProtectionService::FillUserPopulation(
413 const LoginReputationClientRequest::TriggerType& request_type, 475 TriggerType trigger_type,
414 LoginReputationClientRequest* request_proto) { 476 LoginReputationClientRequest* request_proto) {
415 ChromeUserPopulation* user_population = request_proto->mutable_population(); 477 ChromeUserPopulation* user_population = request_proto->mutable_population();
416 user_population->set_user_population( 478 user_population->set_user_population(
417 IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING 479 IsExtendedReporting() ? ChromeUserPopulation::EXTENDED_REPORTING
418 : ChromeUserPopulation::SAFE_BROWSING); 480 : ChromeUserPopulation::SAFE_BROWSING);
419 user_population->set_is_history_sync_enabled(IsHistorySyncEnabled()); 481 user_population->set_is_history_sync_enabled(IsHistorySyncEnabled());
420 482
421 base::FieldTrial* low_reputation_field_trial = 483 base::FieldTrial* low_reputation_field_trial =
422 base::FeatureList::GetFieldTrial(kPasswordFieldOnFocusPinging); 484 base::FeatureList::GetFieldTrial(kPasswordFieldOnFocusPinging);
423 if (low_reputation_field_trial) { 485 if (low_reputation_field_trial) {
424 user_population->add_finch_active_groups( 486 user_population->add_finch_active_groups(
425 low_reputation_field_trial->trial_name() + "|" + 487 low_reputation_field_trial->trial_name() + "|" +
426 low_reputation_field_trial->group_name()); 488 low_reputation_field_trial->group_name());
427 } 489 }
428 base::FieldTrial* password_entry_field_trial = 490 base::FieldTrial* password_entry_field_trial =
429 base::FeatureList::GetFieldTrial(kProtectedPasswordEntryPinging); 491 base::FeatureList::GetFieldTrial(kProtectedPasswordEntryPinging);
430 if (password_entry_field_trial) { 492 if (password_entry_field_trial) {
431 user_population->add_finch_active_groups( 493 user_population->add_finch_active_groups(
432 low_reputation_field_trial->trial_name() + "|" + 494 low_reputation_field_trial->trial_name() + "|" +
433 low_reputation_field_trial->group_name()); 495 low_reputation_field_trial->group_name());
434 } 496 }
435 } 497 }
436 498
437 void PasswordProtectionService::OnURLsDeleted( 499 void PasswordProtectionService::OnURLsDeleted(
438 history::HistoryService* history_service, 500 history::HistoryService* history_service,
439 bool all_history, 501 bool all_history,
440 bool expired, 502 bool expired,
441 const history::URLRows& deleted_rows, 503 const history::URLRows& deleted_rows,
442 const std::set<GURL>& favicon_urls) { 504 const std::set<GURL>& favicon_urls) {
443 if (stored_verdict_count_ <= 0) 505 if (stored_verdict_count_password_on_focus_ <= 0 &&
506 stored_verdict_count_password_entry_ <= 0) {
444 return; 507 return;
508 }
445 509
446 BrowserThread::PostTask( 510 BrowserThread::PostTask(
447 BrowserThread::UI, FROM_HERE, 511 BrowserThread::UI, FROM_HERE,
448 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted, 512 base::Bind(&PasswordProtectionService::RemoveContentSettingsOnURLsDeleted,
449 GetWeakPtr(), all_history, deleted_rows)); 513 GetWeakPtr(), all_history, deleted_rows));
450 } 514 }
451 515
452 void PasswordProtectionService::HistoryServiceBeingDeleted( 516 void PasswordProtectionService::HistoryServiceBeingDeleted(
453 history::HistoryService* history_service) { 517 history::HistoryService* history_service) {
454 history_service_observer_.RemoveAll(); 518 history_service_observer_.RemoveAll();
455 } 519 }
456 520
457 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted( 521 void PasswordProtectionService::RemoveContentSettingsOnURLsDeleted(
458 bool all_history, 522 bool all_history,
459 const history::URLRows& deleted_rows) { 523 const history::URLRows& deleted_rows) {
460 DCHECK_CURRENTLY_ON(BrowserThread::UI); 524 DCHECK_CURRENTLY_ON(BrowserThread::UI);
461 DCHECK(content_settings_); 525 DCHECK(content_settings_);
462 526
463 if (all_history) { 527 if (all_history) {
464 content_settings_->ClearSettingsForOneType( 528 content_settings_->ClearSettingsForOneType(
465 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION); 529 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION);
466 stored_verdict_count_ = 0; 530 stored_verdict_count_password_on_focus_ = 0;
531 stored_verdict_count_password_entry_ = 0;
467 return; 532 return;
468 } 533 }
469 534
470 // For now, if a URL is deleted from history, we simply remove all the 535 // For now, if a URL is deleted from history, we simply remove all the
471 // cached verdicts of the same origin. This is a pretty aggressive deletion. 536 // cached verdicts of the same origin. This is a pretty aggressive deletion.
472 // We might revisit this logic later to decide if we want to only delete the 537 // We might revisit this logic later to decide if we want to only delete the
473 // cached verdict whose cache expression matches this URL. 538 // cached verdict whose cache expression matches this URL.
474 for (const history::URLRow& row : deleted_rows) { 539 for (const history::URLRow& row : deleted_rows) {
475 if (!row.url().SchemeIsHTTPOrHTTPS()) 540 if (!row.url().SchemeIsHTTPOrHTTPS())
476 continue; 541 continue;
542
477 GURL url_key = GetHostNameWithHTTPScheme(row.url()); 543 GURL url_key = GetHostNameWithHTTPScheme(row.url());
478 std::unique_ptr<base::DictionaryValue> verdict_dictionary = 544 stored_verdict_count_password_on_focus_ =
479 base::DictionaryValue::From(content_settings_->GetWebsiteSetting( 545 GetStoredVerdictCount(
480 url_key, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, 546 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) -
481 std::string(), nullptr)); 547 GetVerdictCountForURL(
482 548 url_key, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE);
483 // Move on if we have no cached verdict for this deleted history row. 549 stored_verdict_count_password_entry_ =
484 if (!verdict_dictionary.get() || verdict_dictionary->empty()) 550 GetStoredVerdictCount(
485 continue; 551 LoginReputationClientRequest::PASSWORD_REUSE_EVENT) -
486 552 GetVerdictCountForURL(
487 int verdict_count = static_cast<int>(verdict_dictionary->size()); 553 url_key, LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
488 stored_verdict_count_ = GetStoredVerdictCount() - verdict_count;
489 content_settings_->ClearSettingsForOneTypeWithPredicate( 554 content_settings_->ClearSettingsForOneTypeWithPredicate(
490 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(), 555 CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, base::Time(),
491 base::Bind(&OriginMatchPrimaryPattern, url_key)); 556 base::Bind(&OriginMatchPrimaryPattern, url_key));
492 } 557 }
493 } 558 }
494 559
560 int PasswordProtectionService::GetVerdictCountForURL(const GURL& url,
561 TriggerType trigger_type) {
562 DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
563 trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
564 std::unique_ptr<base::DictionaryValue> cache_dictionary =
565 base::DictionaryValue::From(content_settings_->GetWebsiteSetting(
566 url, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION, std::string(),
567 nullptr));
568 if (!cache_dictionary.get() || cache_dictionary->empty())
569 return 0;
570
571 if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
572 base::DictionaryValue* password_on_focus_dict = nullptr;
573 cache_dictionary->GetDictionaryWithoutPathExpansion(
574 kPasswordOnFocusCacheKey, &password_on_focus_dict);
575 return password_on_focus_dict ? password_on_focus_dict->size() : 0;
576 } else {
577 return cache_dictionary->HasKey(kPasswordOnFocusCacheKey)
578 ? cache_dictionary->size() - 1
579 : cache_dictionary->size();
580 }
581 }
582
583 bool PasswordProtectionService::RemoveExpiredVerdicts(
584 TriggerType trigger_type,
585 base::DictionaryValue* cache_dictionary) {
586 DCHECK(trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE ||
587 trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT);
588 base::DictionaryValue* verdict_dictionary = nullptr;
589 if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT) {
590 verdict_dictionary = cache_dictionary;
591 } else {
592 if (!cache_dictionary->GetDictionaryWithoutPathExpansion(
593 kPasswordOnFocusCacheKey, &verdict_dictionary)) {
594 return false;
595 }
596 }
597
598 if (!verdict_dictionary || verdict_dictionary->empty())
599 return false;
600
601 std::vector<std::string> expired_keys;
602 for (base::DictionaryValue::Iterator it(*verdict_dictionary); !it.IsAtEnd();
603 it.Advance()) {
604 if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT &&
605 it.key() == std::string(kPasswordOnFocusCacheKey))
606 continue;
607
608 base::DictionaryValue* verdict_entry = nullptr;
609 CHECK(verdict_dictionary->GetDictionaryWithoutPathExpansion(
610 it.key(), &verdict_entry));
611 int verdict_received_time;
612 LoginReputationClientResponse verdict;
613 CHECK(ParseVerdictEntry(verdict_entry, &verdict_received_time, &verdict));
614
615 if (IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) {
616 // Since DictionaryValue::Iterator cannot be used to modify the
617 // dictionary, we record the keys of expired verdicts in |expired_keys|
618 // and remove them in the next for-loop.
619 expired_keys.push_back(it.key());
620 }
621 }
622
623 for (const std::string& key : expired_keys) {
624 verdict_dictionary->RemoveWithoutPathExpansion(key, nullptr);
625 if (trigger_type == LoginReputationClientRequest::PASSWORD_REUSE_EVENT)
626 stored_verdict_count_password_entry_--;
627 else
628 stored_verdict_count_password_on_focus_--;
629 }
630
631 if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
632 verdict_dictionary->size() == 0U) {
633 cache_dictionary->RemoveWithoutPathExpansion(kPasswordOnFocusCacheKey,
634 nullptr);
635 }
636
637 return expired_keys.size() > 0U;
638 }
639
495 // static 640 // static
496 bool PasswordProtectionService::ParseVerdictEntry( 641 bool PasswordProtectionService::ParseVerdictEntry(
497 base::DictionaryValue* verdict_entry, 642 base::DictionaryValue* verdict_entry,
498 int* out_verdict_received_time, 643 int* out_verdict_received_time,
499 LoginReputationClientResponse* out_verdict) { 644 LoginReputationClientResponse* out_verdict) {
500 std::string serialized_verdict_proto; 645 std::string serialized_verdict_proto;
501 return verdict_entry && out_verdict && 646 return verdict_entry && out_verdict &&
502 verdict_entry->GetInteger(kCacheCreationTime, 647 verdict_entry->GetInteger(kCacheCreationTime,
503 out_verdict_received_time) && 648 out_verdict_received_time) &&
504 verdict_entry->GetString(kVerdictProto, &serialized_verdict_proto) && 649 verdict_entry->GetString(kVerdictProto, &serialized_verdict_proto) &&
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
585 if (is_password_entry_ping) { 730 if (is_password_entry_ping) {
586 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason, 731 UMA_HISTOGRAM_ENUMERATION(kPasswordEntryRequestOutcomeHistogramName, reason,
587 MAX_OUTCOME); 732 MAX_OUTCOME);
588 } else { 733 } else {
589 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName, 734 UMA_HISTOGRAM_ENUMERATION(kPasswordOnFocusRequestOutcomeHistogramName,
590 reason, MAX_OUTCOME); 735 reason, MAX_OUTCOME);
591 } 736 }
592 } 737 }
593 738
594 } // namespace safe_browsing 739 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698