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.
5 #include "components/autofill/core/common/save_password_progress_logger.h"
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"
18 using base::checked_cast;
20 using base::DictionaryValue;
21 using base::FundamentalValue;
22 using base::StringValue;
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.
34 std::string GetStringFromID(SavePasswordProgressLogger::StringID 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:
44 case SavePasswordProgressLogger::STRING_SCHEME_HTML:
46 case SavePasswordProgressLogger::STRING_SCHEME_BASIC:
48 case SavePasswordProgressLogger::STRING_SCHEME_DIGEST:
50 case SavePasswordProgressLogger::STRING_SCHEME_MESSAGE:
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:
58 case SavePasswordProgressLogger::STRING_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:
70 case SavePasswordProgressLogger::STRING_PASSWORD_GENERATED:
71 return "Password generated";
72 case SavePasswordProgressLogger::STRING_TIMES_USED:
74 case SavePasswordProgressLogger::STRING_USE_ADDITIONAL_AUTHENTICATION:
75 return "Use additional authentication";
76 case SavePasswordProgressLogger::STRING_PSL_MATCH:
78 case SavePasswordProgressLogger::STRING_NAME_OR_ID:
79 return "Form name or ID";
80 case SavePasswordProgressLogger::STRING_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_INVALID:
164 // Intentionally no default: clause here -- all IDs need to get covered.
166 NOTREACHED(); // Win compilers don't believe this is unreachable.
167 return std::string();
170 // Removes privacy sensitive parts of |url| (currently all but host and scheme).
171 std::string ScrubURL(const GURL& url) {
173 return url.GetWithEmptyPath().spec();
174 return std::string();
177 // Returns true for all characters which we don't want to see in the logged IDs
178 // or names of HTML elements.
179 bool IsUnwantedInElementID(char c) {
180 return !(c == '_' || c == '-' || IsAsciiAlpha(c) || IsAsciiDigit(c));
183 // Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns
184 // all characters to lowercase. This damages some valid HTML element IDs or
185 // names, but it is likely that it will be still possible to match the scrubbed
186 // string to the original ID or name in the HTML doc. That's good enough for the
187 // logging purposes, and provides some security benefits.
188 std::string ScrubElementID(std::string element_id) {
190 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' ');
191 return base::StringToLowerASCII(element_id);
194 std::string ScrubElementID(const base::string16& element_id) {
195 return ScrubElementID(base::UTF16ToUTF8(element_id));
198 std::string FormSchemeToString(PasswordForm::Scheme scheme) {
199 SavePasswordProgressLogger::StringID result_id =
200 SavePasswordProgressLogger::STRING_INVALID;
202 case PasswordForm::SCHEME_HTML:
203 result_id = SavePasswordProgressLogger::STRING_SCHEME_HTML;
205 case PasswordForm::SCHEME_BASIC:
206 result_id = SavePasswordProgressLogger::STRING_SCHEME_BASIC;
208 case PasswordForm::SCHEME_DIGEST:
209 result_id = SavePasswordProgressLogger::STRING_SCHEME_DIGEST;
211 case PasswordForm::SCHEME_OTHER:
212 result_id = SavePasswordProgressLogger::STRING_OTHER;
215 return GetStringFromID(result_id);
220 SavePasswordProgressLogger::SavePasswordProgressLogger() {
223 SavePasswordProgressLogger::~SavePasswordProgressLogger() {
226 void SavePasswordProgressLogger::LogPasswordForm(
227 SavePasswordProgressLogger::StringID label,
228 const PasswordForm& form) {
230 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE),
231 FormSchemeToString(form.scheme));
232 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE),
233 FormSchemeToString(form.scheme));
234 log.SetString(GetStringFromID(STRING_SIGNON_REALM),
235 ScrubURL(GURL(form.signon_realm)));
236 log.SetString(GetStringFromID(STRING_ORIGINAL_SIGNON_REALM),
237 ScrubURL(GURL(form.original_signon_realm)));
238 log.SetString(GetStringFromID(STRING_ORIGIN), ScrubURL(form.origin));
239 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(form.action));
240 log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT),
241 ScrubElementID(form.username_element));
242 log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT),
243 ScrubElementID(form.password_element));
244 log.SetBoolean(GetStringFromID(STRING_PASSWORD_AUTOCOMPLETE_SET),
245 form.password_autocomplete_set);
246 log.SetString(GetStringFromID(STRING_NEW_PASSWORD_ELEMENT),
247 ScrubElementID(form.new_password_element));
248 log.SetBoolean(GetStringFromID(STRING_SSL_VALID), form.ssl_valid);
249 log.SetBoolean(GetStringFromID(STRING_PASSWORD_GENERATED),
250 form.type == PasswordForm::TYPE_GENERATED);
251 log.SetInteger(GetStringFromID(STRING_TIMES_USED), form.times_used);
252 log.SetBoolean(GetStringFromID(STRING_USE_ADDITIONAL_AUTHENTICATION),
253 form.use_additional_authentication);
254 log.SetBoolean(GetStringFromID(STRING_PSL_MATCH), form.IsPublicSuffixMatch());
255 LogValue(label, log);
258 void SavePasswordProgressLogger::LogHTMLForm(
259 SavePasswordProgressLogger::StringID label,
260 const std::string& name_or_id,
261 const GURL& action) {
263 log.SetString(GetStringFromID(STRING_NAME_OR_ID), ScrubElementID(name_or_id));
264 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(action));
265 LogValue(label, log);
268 void SavePasswordProgressLogger::LogURL(
269 SavePasswordProgressLogger::StringID label,
271 LogValue(label, StringValue(ScrubURL(url)));
274 void SavePasswordProgressLogger::LogBoolean(
275 SavePasswordProgressLogger::StringID label,
277 LogValue(label, FundamentalValue(truth_value));
280 void SavePasswordProgressLogger::LogNumber(
281 SavePasswordProgressLogger::StringID label,
283 LogValue(label, FundamentalValue(signed_number));
286 void SavePasswordProgressLogger::LogNumber(
287 SavePasswordProgressLogger::StringID label,
288 size_t unsigned_number) {
289 int signed_number = checked_cast<int, size_t>(unsigned_number);
290 LogNumber(label, signed_number);
293 void SavePasswordProgressLogger::LogMessage(
294 SavePasswordProgressLogger::StringID message) {
295 LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message)));
298 void SavePasswordProgressLogger::LogValue(StringID label, const Value& log) {
299 std::string log_string;
300 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions(
301 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string);
302 DCHECK(conversion_to_string_successful);
303 SendLog(GetStringFromID(label) + ": " + log_string);
306 } // namespace autofill