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_OTHER: 43 return "(other)"; 44 case SavePasswordProgressLogger::STRING_SCHEME_HTML: 45 return "HTML"; 46 case SavePasswordProgressLogger::STRING_SCHEME_BASIC: 47 return "Basic"; 48 case SavePasswordProgressLogger::STRING_SCHEME_DIGEST: 49 return "Digest"; 50 case SavePasswordProgressLogger::STRING_SCHEME_MESSAGE: 51 return "Scheme"; 52 case SavePasswordProgressLogger::STRING_SIGNON_REALM: 53 return "Signon realm"; 54 case SavePasswordProgressLogger::STRING_ORIGINAL_SIGNON_REALM: 55 return "Original signon realm"; 56 case SavePasswordProgressLogger::STRING_ORIGIN: 57 return "Origin"; 58 case SavePasswordProgressLogger::STRING_ACTION: 59 return "Action"; 60 case SavePasswordProgressLogger::STRING_USERNAME_ELEMENT: 61 return "Username element"; 62 case SavePasswordProgressLogger::STRING_PASSWORD_ELEMENT: 63 return "Password element"; 64 case SavePasswordProgressLogger::STRING_PASSWORD_AUTOCOMPLETE_SET: 65 return "Password autocomplete set"; 66 case SavePasswordProgressLogger::STRING_NEW_PASSWORD_ELEMENT: 67 return "New password element"; 68 case SavePasswordProgressLogger::STRING_SSL_VALID: 69 return "SSL valid"; 70 case SavePasswordProgressLogger::STRING_PASSWORD_GENERATED: 71 return "Password generated"; 72 case SavePasswordProgressLogger::STRING_TIMES_USED: 73 return "Times used"; 74 case SavePasswordProgressLogger::STRING_USE_ADDITIONAL_AUTHENTICATION: 75 return "Use additional authentication"; 76 case SavePasswordProgressLogger::STRING_PSL_MATCH: 77 return "PSL match"; 78 case SavePasswordProgressLogger::STRING_NAME_OR_ID: 79 return "Form name or ID"; 80 case SavePasswordProgressLogger::STRING_MESSAGE: 81 return "Message"; 82 case SavePasswordProgressLogger::STRING_SET_AUTH_METHOD: 83 return "LoginHandler::SetAuth"; 84 case SavePasswordProgressLogger::STRING_AUTHENTICATION_HANDLED: 85 return "Authentication already handled"; 86 case SavePasswordProgressLogger::STRING_LOGINHANDLER_FORM: 87 return "LoginHandler reports this form"; 88 case SavePasswordProgressLogger::STRING_SEND_PASSWORD_FORMS_METHOD: 89 return "PasswordAutofillAgent::SendPasswordForms"; 90 case SavePasswordProgressLogger::STRING_SECURITY_ORIGIN: 91 return "Security origin"; 92 case SavePasswordProgressLogger::STRING_SECURITY_ORIGIN_FAILURE: 93 return "Security origin cannot access password manager."; 94 case SavePasswordProgressLogger::STRING_WEBPAGE_EMPTY: 95 return "Webpage is empty."; 96 case SavePasswordProgressLogger::STRING_NUMBER_OF_ALL_FORMS: 97 return "Number of all forms"; 98 case SavePasswordProgressLogger::STRING_FORM_FOUND_ON_PAGE: 99 return "Form found on page"; 100 case SavePasswordProgressLogger::STRING_FORM_IS_VISIBLE: 101 return "Form is visible"; 102 case SavePasswordProgressLogger::STRING_FORM_IS_PASSWORD: 103 return "Form is a password form"; 104 case SavePasswordProgressLogger::STRING_WILL_SUBMIT_FORM_METHOD: 105 return "PasswordAutofillAgent::WillSubmitForm"; 106 case SavePasswordProgressLogger::STRING_HTML_FORM_FOR_SUBMIT: 107 return "HTML form for submit"; 108 case SavePasswordProgressLogger::STRING_CREATED_PASSWORD_FORM: 109 return "Created PasswordForm"; 110 case SavePasswordProgressLogger::STRING_SUBMITTED_PASSWORD_REPLACED: 111 return "Submitted password replaced with the provisionally saved one."; 112 case SavePasswordProgressLogger::STRING_DID_START_PROVISIONAL_LOAD_METHOD: 113 return "PasswordAutofillAgent::DidStartProvisionalLoad"; 114 case SavePasswordProgressLogger::STRING_FORM_FRAME_EQ_FRAME: 115 return "form_frame == frame"; 116 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM_FOR_FRAME: 117 return "provisionally_saved_forms_[form_frame]"; 118 case SavePasswordProgressLogger::STRING_PASSWORD_FORM_FOUND_ON_PAGE: 119 return "PasswordForm found on the page"; 120 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVE_PASSWORD_METHOD: 121 return "PasswordManager::ProvisionallySavePassword"; 122 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVE_PASSWORD_FORM: 123 return "ProvisionallySavePassword form"; 124 case SavePasswordProgressLogger::STRING_IS_SAVING_ENABLED: 125 return "IsSavingEnabledForCurrentPage"; 126 case SavePasswordProgressLogger::STRING_EMPTY_PASSWORD: 127 return "Empty password"; 128 case SavePasswordProgressLogger::STRING_EXACT_MATCH: 129 return "Form manager found, exact match."; 130 case SavePasswordProgressLogger::STRING_MATCH_WITHOUT_ACTION: 131 return "Form manager found, match except for action."; 132 case SavePasswordProgressLogger::STRING_MATCHING_NOT_COMPLETE: 133 return "No form manager has completed matching."; 134 case SavePasswordProgressLogger::STRING_FORM_BLACKLISTED: 135 return "Form blacklisted."; 136 case SavePasswordProgressLogger::STRING_INVALID_FORM: 137 return "Invalid form."; 138 case SavePasswordProgressLogger::STRING_AUTOCOMPLETE_OFF: 139 return "Autocomplete=off."; 140 case SavePasswordProgressLogger::STRING_SYNC_CREDENTIAL: 141 return "Credential is used for syncing passwords."; 142 case SavePasswordProgressLogger::STRING_PROVISIONALLY_SAVED_FORM: 143 return "provisionally_saved_form"; 144 case SavePasswordProgressLogger::STRING_IGNORE_POSSIBLE_USERNAMES: 145 return "Ignore other possible usernames"; 146 case SavePasswordProgressLogger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD: 147 return "PasswordManager::OnPasswordFormsRendered"; 148 case SavePasswordProgressLogger::STRING_NO_PROVISIONAL_SAVE_MANAGER: 149 return "No provisional save manager"; 150 case SavePasswordProgressLogger::STRING_NUMBER_OF_VISIBLE_FORMS: 151 return "Number of visible forms"; 152 case SavePasswordProgressLogger::STRING_PASSWORD_FORM_REAPPEARED: 153 return "Password form re-appeared"; 154 case SavePasswordProgressLogger::STRING_SAVING_DISABLED: 155 return "Saving disabled"; 156 case SavePasswordProgressLogger::STRING_NO_MATCHING_FORM: 157 return "No matching form"; 158 case SavePasswordProgressLogger::STRING_SSL_ERRORS_PRESENT: 159 return "SSL errors present"; 160 case SavePasswordProgressLogger::STRING_ONLY_VISIBLE: 161 return "only_visible"; 162 case SavePasswordProgressLogger::STRING_SHOW_PASSWORD_PROMPT: 163 return "Show password prompt"; 164 case SavePasswordProgressLogger::STRING_INVALID: 165 return "INVALID"; 166 // Intentionally no default: clause here -- all IDs need to get covered. 167 } 168 NOTREACHED(); // Win compilers don't believe this is unreachable. 169 return std::string(); 170}; 171 172// Removes privacy sensitive parts of |url| (currently all but host and scheme). 173std::string ScrubURL(const GURL& url) { 174 if (url.is_valid()) 175 return url.GetWithEmptyPath().spec(); 176 return std::string(); 177} 178 179// Returns true for all characters which we don't want to see in the logged IDs 180// or names of HTML elements. 181bool IsUnwantedInElementID(char c) { 182 return !(c == '_' || c == '-' || IsAsciiAlpha(c) || IsAsciiDigit(c)); 183} 184 185// Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns 186// all characters to lowercase. This damages some valid HTML element IDs or 187// names, but it is likely that it will be still possible to match the scrubbed 188// string to the original ID or name in the HTML doc. That's good enough for the 189// logging purposes, and provides some security benefits. 190std::string ScrubElementID(std::string element_id) { 191 std::replace_if( 192 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); 193 return base::StringToLowerASCII(element_id); 194} 195 196std::string ScrubElementID(const base::string16& element_id) { 197 return ScrubElementID(base::UTF16ToUTF8(element_id)); 198} 199 200std::string FormSchemeToString(PasswordForm::Scheme scheme) { 201 SavePasswordProgressLogger::StringID result_id = 202 SavePasswordProgressLogger::STRING_INVALID; 203 switch (scheme) { 204 case PasswordForm::SCHEME_HTML: 205 result_id = SavePasswordProgressLogger::STRING_SCHEME_HTML; 206 break; 207 case PasswordForm::SCHEME_BASIC: 208 result_id = SavePasswordProgressLogger::STRING_SCHEME_BASIC; 209 break; 210 case PasswordForm::SCHEME_DIGEST: 211 result_id = SavePasswordProgressLogger::STRING_SCHEME_DIGEST; 212 break; 213 case PasswordForm::SCHEME_OTHER: 214 result_id = SavePasswordProgressLogger::STRING_OTHER; 215 break; 216 } 217 return GetStringFromID(result_id); 218} 219 220} // namespace 221 222SavePasswordProgressLogger::SavePasswordProgressLogger() { 223} 224 225SavePasswordProgressLogger::~SavePasswordProgressLogger() { 226} 227 228void SavePasswordProgressLogger::LogPasswordForm( 229 SavePasswordProgressLogger::StringID label, 230 const PasswordForm& form) { 231 DictionaryValue log; 232 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), 233 FormSchemeToString(form.scheme)); 234 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), 235 FormSchemeToString(form.scheme)); 236 log.SetString(GetStringFromID(STRING_SIGNON_REALM), 237 ScrubURL(GURL(form.signon_realm))); 238 log.SetString(GetStringFromID(STRING_ORIGINAL_SIGNON_REALM), 239 ScrubURL(GURL(form.original_signon_realm))); 240 log.SetString(GetStringFromID(STRING_ORIGIN), ScrubURL(form.origin)); 241 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(form.action)); 242 log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT), 243 ScrubElementID(form.username_element)); 244 log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT), 245 ScrubElementID(form.password_element)); 246 log.SetBoolean(GetStringFromID(STRING_PASSWORD_AUTOCOMPLETE_SET), 247 form.password_autocomplete_set); 248 log.SetString(GetStringFromID(STRING_NEW_PASSWORD_ELEMENT), 249 ScrubElementID(form.new_password_element)); 250 log.SetBoolean(GetStringFromID(STRING_SSL_VALID), form.ssl_valid); 251 log.SetBoolean(GetStringFromID(STRING_PASSWORD_GENERATED), 252 form.type == PasswordForm::TYPE_GENERATED); 253 log.SetInteger(GetStringFromID(STRING_TIMES_USED), form.times_used); 254 log.SetBoolean(GetStringFromID(STRING_USE_ADDITIONAL_AUTHENTICATION), 255 form.use_additional_authentication); 256 log.SetBoolean(GetStringFromID(STRING_PSL_MATCH), form.IsPublicSuffixMatch()); 257 LogValue(label, log); 258} 259 260void SavePasswordProgressLogger::LogHTMLForm( 261 SavePasswordProgressLogger::StringID label, 262 const std::string& name_or_id, 263 const GURL& action) { 264 DictionaryValue log; 265 log.SetString(GetStringFromID(STRING_NAME_OR_ID), ScrubElementID(name_or_id)); 266 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(action)); 267 LogValue(label, log); 268} 269 270void SavePasswordProgressLogger::LogURL( 271 SavePasswordProgressLogger::StringID label, 272 const GURL& url) { 273 LogValue(label, StringValue(ScrubURL(url))); 274} 275 276void SavePasswordProgressLogger::LogBoolean( 277 SavePasswordProgressLogger::StringID label, 278 bool truth_value) { 279 LogValue(label, FundamentalValue(truth_value)); 280} 281 282void SavePasswordProgressLogger::LogNumber( 283 SavePasswordProgressLogger::StringID label, 284 int signed_number) { 285 LogValue(label, FundamentalValue(signed_number)); 286} 287 288void SavePasswordProgressLogger::LogNumber( 289 SavePasswordProgressLogger::StringID label, 290 size_t unsigned_number) { 291 int signed_number = checked_cast<int, size_t>(unsigned_number); 292 LogNumber(label, signed_number); 293} 294 295void SavePasswordProgressLogger::LogMessage( 296 SavePasswordProgressLogger::StringID message) { 297 LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message))); 298} 299 300void SavePasswordProgressLogger::LogValue(StringID label, const Value& log) { 301 std::string log_string; 302 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( 303 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); 304 DCHECK(conversion_to_string_successful); 305 SendLog(GetStringFromID(label) + ": " + log_string); 306} 307 308} // namespace autofill 309