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