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

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

Powered by Google App Engine
This is Rietveld 408576698