• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using base::checked_cast;
19 using base::Value;
20 using base::DictionaryValue;
21 using base::FundamentalValue;
22 using base::StringValue;
23 
24 namespace autofill {
25 
26 namespace {
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.
GetStringFromID(SavePasswordProgressLogger::StringID id)34 std::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).
ScrubURL(const GURL & url)177 std::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.
IsUnwantedInElementID(char c)185 bool 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.
ScrubElementID(std::string element_id)194 std::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 
ScrubElementID(const base::string16 & element_id)200 std::string ScrubElementID(const base::string16& element_id) {
201   return ScrubElementID(base::UTF16ToUTF8(element_id));
202 }
203 
FormSchemeToString(PasswordForm::Scheme scheme)204 std::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 
FormMethodToString(const std::string & method)224 std::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 
SavePasswordProgressLogger()241 SavePasswordProgressLogger::SavePasswordProgressLogger() {
242 }
243 
~SavePasswordProgressLogger()244 SavePasswordProgressLogger::~SavePasswordProgressLogger() {
245 }
246 
LogPasswordForm(SavePasswordProgressLogger::StringID label,const PasswordForm & form)247 void 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 
LogHTMLForm(SavePasswordProgressLogger::StringID label,const std::string & name_or_id,const std::string & method,const GURL & action)279 void 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 
LogURL(SavePasswordProgressLogger::StringID label,const GURL & url)291 void SavePasswordProgressLogger::LogURL(
292     SavePasswordProgressLogger::StringID label,
293     const GURL& url) {
294   LogValue(label, StringValue(ScrubURL(url)));
295 }
296 
LogBoolean(SavePasswordProgressLogger::StringID label,bool truth_value)297 void SavePasswordProgressLogger::LogBoolean(
298     SavePasswordProgressLogger::StringID label,
299     bool truth_value) {
300   LogValue(label, FundamentalValue(truth_value));
301 }
302 
LogNumber(SavePasswordProgressLogger::StringID label,int signed_number)303 void SavePasswordProgressLogger::LogNumber(
304     SavePasswordProgressLogger::StringID label,
305     int signed_number) {
306   LogValue(label, FundamentalValue(signed_number));
307 }
308 
LogNumber(SavePasswordProgressLogger::StringID label,size_t unsigned_number)309 void 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 
LogMessage(SavePasswordProgressLogger::StringID message)316 void SavePasswordProgressLogger::LogMessage(
317     SavePasswordProgressLogger::StringID message) {
318   LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message)));
319 }
320 
LogValue(StringID label,const Value & log)321 void 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