save_password_progress_logger.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "components/autofill/core/common/save_password_progress_logger.h" 6 7#include <algorithm> 8 9#include "base/json/json_writer.h" 10#include "base/logging.h" 11#include "base/numerics/safe_conversions.h" 12#include "base/strings/string16.h" 13#include "base/strings/string_util.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/values.h" 16#include "components/autofill/core/common/password_form.h" 17 18using base::checked_cast; 19using base::Value; 20using base::DictionaryValue; 21using base::FundamentalValue; 22using base::StringValue; 23 24namespace autofill { 25 26namespace { 27 28// Note 1: Caching the ID->string map in an array would be probably faster, but 29// the switch statement is (a) robust against re-ordering, and (b) checks in 30// compile-time, that all IDs get a string assigned. The expected frequency of 31// calls is low enough (in particular, zero if password manager internals page 32// is not open), that optimizing for code robustness is preferred against speed. 33// Note 2: The messages can be used as dictionary keys. Do not use '.' in them. 34std::string GetStringFromID(SavePasswordProgressLogger::StringID id) { 35 switch (id) { 36 case SavePasswordProgressLogger::STRING_DECISION_ASK: 37 return "Decision: ASK the user"; 38 case SavePasswordProgressLogger::STRING_DECISION_DROP: 39 return "Decision: DROP the password"; 40 case SavePasswordProgressLogger::STRING_DECISION_SAVE: 41 return "Decision: SAVE the password"; 42 case SavePasswordProgressLogger::STRING_METHOD: 43 return "Form method"; 44 case SavePasswordProgressLogger::STRING_METHOD_GET: 45 return "GET"; 46 case SavePasswordProgressLogger::STRING_METHOD_POST: 47 return "POST"; 48 case SavePasswordProgressLogger::STRING_METHOD_EMPTY: 49 return "(empty)"; 50 case SavePasswordProgressLogger::STRING_OTHER: 51 return "(other)"; 52 case SavePasswordProgressLogger::STRING_SCHEME_HTML: 53 return "HTML"; 54 case SavePasswordProgressLogger::STRING_SCHEME_BASIC: 55 return "Basic"; 56 case SavePasswordProgressLogger::STRING_SCHEME_DIGEST: 57 return "Digest"; 58 case SavePasswordProgressLogger::STRING_SCHEME_MESSAGE: 59 return "Scheme"; 60 case SavePasswordProgressLogger::STRING_SIGNON_REALM: 61 return "Signon realm"; 62 case SavePasswordProgressLogger::STRING_ORIGINAL_SIGNON_REALM: 63 return "Original signon realm"; 64 case SavePasswordProgressLogger::STRING_ORIGIN: 65 return "Origin"; 66 case SavePasswordProgressLogger::STRING_ACTION: 67 return "Action"; 68 case SavePasswordProgressLogger::STRING_USERNAME_ELEMENT: 69 return "Username element"; 70 case SavePasswordProgressLogger::STRING_PASSWORD_ELEMENT: 71 return "Password element"; 72 case SavePasswordProgressLogger::STRING_PASSWORD_AUTOCOMPLETE_SET: 73 return "Password autocomplete set"; 74 case SavePasswordProgressLogger::STRING_OLD_PASSWORD_ELEMENT: 75 return "Old password element"; 76 case SavePasswordProgressLogger::STRING_SSL_VALID: 77 return "SSL valid"; 78 case SavePasswordProgressLogger::STRING_PASSWORD_GENERATED: 79 return "Password generated"; 80 case SavePasswordProgressLogger::STRING_TIMES_USED: 81 return "Times used"; 82 case SavePasswordProgressLogger::STRING_USE_ADDITIONAL_AUTHENTICATION: 83 return "Use additional authentication"; 84 case SavePasswordProgressLogger::STRING_PSL_MATCH: 85 return "PSL match"; 86 case SavePasswordProgressLogger::STRING_NAME_OR_ID: 87 return "Form name or ID"; 88 case SavePasswordProgressLogger::STRING_MESSAGE: 89 return "Message"; 90 case SavePasswordProgressLogger::STRING_SET_AUTH_METHOD: 91 return "LoginHandler::SetAuth"; 92 case SavePasswordProgressLogger::STRING_AUTHENTICATION_HANDLED: 93 return "Authentication already handled"; 94 case SavePasswordProgressLogger::STRING_LOGINHANDLER_FORM: 95 return "LoginHandler reports this form"; 96 case SavePasswordProgressLogger::STRING_SEND_PASSWORD_FORMS_METHOD: 97 return "PasswordAutofillAgent::SendPasswordForms"; 98 case SavePasswordProgressLogger::STRING_SECURITY_ORIGIN: 99 return "Security origin"; 100 case SavePasswordProgressLogger::STRING_SECURITY_ORIGIN_FAILURE: 101 return "Security origin cannot access password manager."; 102 case SavePasswordProgressLogger::STRING_WEBPAGE_EMPTY: 103 return "Webpage is empty."; 104 case SavePasswordProgressLogger::STRING_NUMBER_OF_ALL_FORMS: 105 return "Number of all forms"; 106 case SavePasswordProgressLogger::STRING_FORM_FOUND_ON_PAGE: 107 return "Form found on page"; 108 case SavePasswordProgressLogger::STRING_FORM_IS_VISIBLE: 109 return "Form is visible"; 110 case SavePasswordProgressLogger::STRING_FORM_IS_PASSWORD: 111 return "Form is a password form"; 112 case SavePasswordProgressLogger::STRING_WILL_SUBMIT_FORM_METHOD: 113 return "PasswordAutofillAgent::WillSubmitForm"; 114 case SavePasswordProgressLogger::STRING_HTML_FORM_FOR_SUBMIT: 115 return "HTML form for submit"; 116 case SavePasswordProgressLogger::STRING_CREATED_PASSWORD_FORM: 117 return "Created PasswordForm"; 118 case SavePasswordProgressLogger::STRING_SUBMITTED_PASSWORD_REPLACED: 119 return "Submitted password replaced with the provisionally saved one."; 120 case SavePasswordProgressLogger::STRING_DID_START_PROVISIONAL_LOAD_METHOD: 121 return "PasswordAutofillAgent::DidStartProvisionalLoad"; 122 case SavePasswordProgressLogger::STRING_FORM_FRAME_EQ_FRAME: 123 return "form_frame == frame"; 124 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM_FOR_FRAME: 125 return "provisionally_saved_forms_[form_frame]"; 126 case SavePasswordProgressLogger::STRING_PASSWORD_FORM_FOUND_ON_PAGE: 127 return "PasswordForm found on the page"; 128 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVE_PASSWORD_METHOD: 129 return "PasswordManager::ProvisionallySavePassword"; 130 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVE_PASSWORD_FORM: 131 return "ProvisionallySavePassword form"; 132 case SavePasswordProgressLogger::STRING_IS_SAVING_ENABLED: 133 return "IsSavingEnabledForCurrentPage"; 134 case SavePasswordProgressLogger::STRING_EMPTY_PASSWORD: 135 return "Empty password"; 136 case SavePasswordProgressLogger::STRING_EXACT_MATCH: 137 return "Form manager found, exact match."; 138 case SavePasswordProgressLogger::STRING_MATCH_WITHOUT_ACTION: 139 return "Form manager found, match except for action."; 140 case SavePasswordProgressLogger::STRING_NO_FORM_MANAGER: 141 return "No form manager found."; 142 case SavePasswordProgressLogger::STRING_FORM_BLACKLISTED: 143 return "Form blacklisted."; 144 case SavePasswordProgressLogger::STRING_INVALID_FORM: 145 return "Invalid form."; 146 case SavePasswordProgressLogger::STRING_AUTOCOMPLETE_OFF: 147 return "Autocomplete=off."; 148 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM: 149 return "provisionally_saved_form"; 150 case SavePasswordProgressLogger::STRING_IGNORE_POSSIBLE_USERNAMES: 151 return "Ignore other possible usernames"; 152 case SavePasswordProgressLogger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD: 153 return "PasswordManager::OnPasswordFormsRendered"; 154 case SavePasswordProgressLogger::STRING_NO_PROVISIONAL_SAVE_MANAGER: 155 return "No provisional save manager"; 156 case SavePasswordProgressLogger::STRING_NUMBER_OF_VISIBLE_FORMS: 157 return "Number of visible forms"; 158 case SavePasswordProgressLogger::STRING_PASSWORD_FORM_REAPPEARED: 159 return "Password form re-appeared"; 160 case SavePasswordProgressLogger::STRING_SAVING_DISABLED: 161 return "Saving disabled"; 162 case SavePasswordProgressLogger::STRING_NO_MATCHING_FORM: 163 return "No matching form"; 164 case SavePasswordProgressLogger::STRING_SSL_ERRORS_PRESENT: 165 return "SSL errors present"; 166 case SavePasswordProgressLogger::STRING_ONLY_VISIBLE: 167 return "only_visible"; 168 case SavePasswordProgressLogger::STRING_INVALID: 169 return "INVALID"; 170 // Intentionally no default: clause here -- all IDs need to get covered. 171 } 172 NOTREACHED(); // Win compilers don't believe this is unreachable. 173 return std::string(); 174}; 175 176// Removes privacy sensitive parts of |url| (currently all but host and scheme). 177std::string ScrubURL(const GURL& url) { 178 if (url.is_valid()) 179 return url.GetWithEmptyPath().spec(); 180 return std::string(); 181} 182 183// Returns true for all characters which we don't want to see in the logged IDs 184// or names of HTML elements. 185bool IsUnwantedInElementID(char c) { 186 return !(c == '_' || c == '-' || IsAsciiAlpha(c) || IsAsciiDigit(c)); 187} 188 189// Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns 190// all characters to lowercase. This damages some valid HTML element IDs or 191// names, but it is likely that it will be still possible to match the scrubbed 192// string to the original ID or name in the HTML doc. That's good enough for the 193// logging purposes, and provides some security benefits. 194std::string ScrubElementID(std::string element_id) { 195 std::replace_if( 196 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); 197 return StringToLowerASCII(element_id); 198} 199 200std::string ScrubElementID(const base::string16& element_id) { 201 return ScrubElementID(base::UTF16ToUTF8(element_id)); 202} 203 204std::string FormSchemeToString(PasswordForm::Scheme scheme) { 205 SavePasswordProgressLogger::StringID result_id = 206 SavePasswordProgressLogger::STRING_INVALID; 207 switch (scheme) { 208 case PasswordForm::SCHEME_HTML: 209 result_id = SavePasswordProgressLogger::STRING_SCHEME_HTML; 210 break; 211 case PasswordForm::SCHEME_BASIC: 212 result_id = SavePasswordProgressLogger::STRING_SCHEME_BASIC; 213 break; 214 case PasswordForm::SCHEME_DIGEST: 215 result_id = SavePasswordProgressLogger::STRING_SCHEME_DIGEST; 216 break; 217 case PasswordForm::SCHEME_OTHER: 218 result_id = SavePasswordProgressLogger::STRING_OTHER; 219 break; 220 } 221 return GetStringFromID(result_id); 222} 223 224std::string FormMethodToString(const std::string& method) { 225 std::string method_processed; 226 base::TrimWhitespaceASCII( 227 StringToLowerASCII(method), base::TRIM_ALL, &method_processed); 228 SavePasswordProgressLogger::StringID result_id = 229 SavePasswordProgressLogger::STRING_OTHER; 230 if (method_processed.empty()) 231 result_id = SavePasswordProgressLogger::STRING_METHOD_EMPTY; 232 else if (method_processed == "get") 233 result_id = SavePasswordProgressLogger::STRING_METHOD_GET; 234 else if (method_processed == "post") 235 result_id = SavePasswordProgressLogger::STRING_METHOD_POST; 236 return GetStringFromID(result_id); 237} 238 239} // namespace 240 241SavePasswordProgressLogger::SavePasswordProgressLogger() { 242} 243 244SavePasswordProgressLogger::~SavePasswordProgressLogger() { 245} 246 247void SavePasswordProgressLogger::LogPasswordForm( 248 SavePasswordProgressLogger::StringID label, 249 const PasswordForm& form) { 250 DictionaryValue log; 251 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), 252 FormSchemeToString(form.scheme)); 253 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), 254 FormSchemeToString(form.scheme)); 255 log.SetString(GetStringFromID(STRING_SIGNON_REALM), 256 ScrubURL(GURL(form.signon_realm))); 257 log.SetString(GetStringFromID(STRING_ORIGINAL_SIGNON_REALM), 258 ScrubURL(GURL(form.original_signon_realm))); 259 log.SetString(GetStringFromID(STRING_ORIGIN), ScrubURL(form.origin)); 260 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(form.action)); 261 log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT), 262 ScrubElementID(form.username_element)); 263 log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT), 264 ScrubElementID(form.password_element)); 265 log.SetBoolean(GetStringFromID(STRING_PASSWORD_AUTOCOMPLETE_SET), 266 form.password_autocomplete_set); 267 log.SetString(GetStringFromID(STRING_OLD_PASSWORD_ELEMENT), 268 ScrubElementID(form.old_password_element)); 269 log.SetBoolean(GetStringFromID(STRING_SSL_VALID), form.ssl_valid); 270 log.SetBoolean(GetStringFromID(STRING_PASSWORD_GENERATED), 271 form.type == PasswordForm::TYPE_GENERATED); 272 log.SetInteger(GetStringFromID(STRING_TIMES_USED), form.times_used); 273 log.SetBoolean(GetStringFromID(STRING_USE_ADDITIONAL_AUTHENTICATION), 274 form.use_additional_authentication); 275 log.SetBoolean(GetStringFromID(STRING_PSL_MATCH), form.IsPublicSuffixMatch()); 276 LogValue(label, log); 277} 278 279void SavePasswordProgressLogger::LogHTMLForm( 280 SavePasswordProgressLogger::StringID label, 281 const std::string& name_or_id, 282 const std::string& method, 283 const GURL& action) { 284 DictionaryValue log; 285 log.SetString(GetStringFromID(STRING_NAME_OR_ID), ScrubElementID(name_or_id)); 286 log.SetString(GetStringFromID(STRING_METHOD), FormMethodToString(method)); 287 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(action)); 288 LogValue(label, log); 289} 290 291void SavePasswordProgressLogger::LogURL( 292 SavePasswordProgressLogger::StringID label, 293 const GURL& url) { 294 LogValue(label, StringValue(ScrubURL(url))); 295} 296 297void SavePasswordProgressLogger::LogBoolean( 298 SavePasswordProgressLogger::StringID label, 299 bool truth_value) { 300 LogValue(label, FundamentalValue(truth_value)); 301} 302 303void SavePasswordProgressLogger::LogNumber( 304 SavePasswordProgressLogger::StringID label, 305 int signed_number) { 306 LogValue(label, FundamentalValue(signed_number)); 307} 308 309void SavePasswordProgressLogger::LogNumber( 310 SavePasswordProgressLogger::StringID label, 311 size_t unsigned_number) { 312 int signed_number = checked_cast<int, size_t>(unsigned_number); 313 LogNumber(label, signed_number); 314} 315 316void SavePasswordProgressLogger::LogMessage( 317 SavePasswordProgressLogger::StringID message) { 318 LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message))); 319} 320 321void SavePasswordProgressLogger::LogValue(StringID label, const Value& log) { 322 std::string log_string; 323 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( 324 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); 325 DCHECK(conversion_to_string_successful); 326 SendLog(GetStringFromID(label) + ": " + log_string); 327} 328 329} // namespace autofill 330