OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/autofill/core/common/save_password_progress_logger.h" | 5 #include "components/autofill/core/common/save_password_progress_logger.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 |
7 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
8 #include "base/logging.h" | 10 #include "base/logging.h" |
9 #include "base/numerics/safe_conversions.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" |
10 #include "base/values.h" | 15 #include "base/values.h" |
11 #include "components/autofill/core/common/password_form.h" | 16 #include "components/autofill/core/common/password_form.h" |
12 | 17 |
13 using base::checked_cast; | 18 using base::checked_cast; |
14 using base::Value; | 19 using base::Value; |
15 using base::DictionaryValue; | 20 using base::DictionaryValue; |
16 using base::FundamentalValue; | 21 using base::FundamentalValue; |
17 using base::StringValue; | 22 using base::StringValue; |
18 | 23 |
19 namespace autofill { | 24 namespace autofill { |
20 | 25 |
21 namespace { | 26 namespace { |
22 | 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. |
| 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_INVALID: |
| 91 return "INVALID"; |
| 92 // Intentionally no default: clause here -- all IDs need to get covered. |
| 93 } |
| 94 NOTREACHED(); // Win compilers don't believe this is unreachable. |
| 95 return std::string(); |
| 96 }; |
| 97 |
23 // Removes privacy sensitive parts of |url| (currently all but host and scheme). | 98 // Removes privacy sensitive parts of |url| (currently all but host and scheme). |
24 std::string ScrubURL(const GURL& url) { | 99 std::string ScrubURL(const GURL& url) { |
25 if (url.is_valid()) | 100 if (url.is_valid()) |
26 return url.GetWithEmptyPath().spec(); | 101 return url.GetWithEmptyPath().spec(); |
27 return std::string(); | 102 return std::string(); |
28 } | 103 } |
29 | 104 |
| 105 // Returns true for all characters which we don't want to see in the logged IDs |
| 106 // or names of HTML elements. |
| 107 bool IsUnwantedInElementID(char c) { |
| 108 return !(c == '_' || c == '-' || IsAsciiAlpha(c) || IsAsciiDigit(c)); |
| 109 } |
| 110 |
| 111 // Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns |
| 112 // all characters to lowercase. This damages some valid HTML element IDs or |
| 113 // names, but it is likely that it will be still possible to match the scrubbed |
| 114 // string to the original ID or name in the HTML doc. That's good enough for the |
| 115 // logging purposes, and provides some security benefits. |
| 116 std::string ScrubElementID(std::string element_id) { |
| 117 std::replace_if( |
| 118 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); |
| 119 return StringToLowerASCII(element_id); |
| 120 } |
| 121 |
| 122 std::string ScrubElementID(const base::string16& element_id) { |
| 123 return ScrubElementID(base::UTF16ToUTF8(element_id)); |
| 124 } |
| 125 |
30 std::string FormSchemeToString(PasswordForm::Scheme scheme) { | 126 std::string FormSchemeToString(PasswordForm::Scheme scheme) { |
| 127 SavePasswordProgressLogger::StringID result_id = |
| 128 SavePasswordProgressLogger::STRING_INVALID; |
31 switch (scheme) { | 129 switch (scheme) { |
32 case PasswordForm::SCHEME_HTML: | 130 case PasswordForm::SCHEME_HTML: |
33 return "HTML"; | 131 result_id = SavePasswordProgressLogger::STRING_SCHEME_HTML; |
| 132 break; |
34 case PasswordForm::SCHEME_BASIC: | 133 case PasswordForm::SCHEME_BASIC: |
35 return "BASIC"; | 134 result_id = SavePasswordProgressLogger::STRING_SCHEME_BASIC; |
| 135 break; |
36 case PasswordForm::SCHEME_DIGEST: | 136 case PasswordForm::SCHEME_DIGEST: |
37 return "DIGEST"; | 137 result_id = SavePasswordProgressLogger::STRING_SCHEME_DIGEST; |
| 138 break; |
38 case PasswordForm::SCHEME_OTHER: | 139 case PasswordForm::SCHEME_OTHER: |
39 return "OTHER"; | 140 result_id = SavePasswordProgressLogger::STRING_OTHER; |
| 141 break; |
40 } | 142 } |
41 NOTREACHED(); // Win compilers don't believe this is unreachable. | 143 return GetStringFromID(result_id); |
42 return std::string(); | |
43 } | 144 } |
44 | 145 |
45 StringValue DecisionToStringValue( | 146 std::string FormMethodToString(const std::string& method) { |
46 SavePasswordProgressLogger::Decision decision) { | 147 std::string method_processed; |
47 switch (decision) { | 148 base::TrimWhitespaceASCII( |
48 case SavePasswordProgressLogger::DECISION_SAVE: | 149 StringToLowerASCII(method), base::TRIM_ALL, &method_processed); |
49 return StringValue("SAVE the password"); | 150 SavePasswordProgressLogger::StringID result_id = |
50 case SavePasswordProgressLogger::DECISION_ASK: | 151 SavePasswordProgressLogger::STRING_OTHER; |
51 return StringValue("ASK the user whether to save the password"); | 152 if (method_processed.empty()) |
52 case SavePasswordProgressLogger::DECISION_DROP: | 153 result_id = SavePasswordProgressLogger::STRING_METHOD_EMPTY; |
53 return StringValue("DROP the password"); | 154 else if (method_processed == "get") |
54 } | 155 result_id = SavePasswordProgressLogger::STRING_METHOD_GET; |
55 NOTREACHED(); // Win compilers don't believe this is unreachable. | 156 else if (method_processed == "post") |
56 return StringValue(std::string()); | 157 result_id = SavePasswordProgressLogger::STRING_METHOD_POST; |
| 158 return GetStringFromID(result_id); |
57 } | 159 } |
58 | 160 |
59 } // namespace | 161 } // namespace |
60 | 162 |
61 SavePasswordProgressLogger::SavePasswordProgressLogger() {} | 163 SavePasswordProgressLogger::SavePasswordProgressLogger() { |
62 | |
63 SavePasswordProgressLogger::~SavePasswordProgressLogger() {} | |
64 | |
65 void SavePasswordProgressLogger::LogPasswordForm(const std::string& message, | |
66 const PasswordForm& form) { | |
67 DictionaryValue log; | |
68 // Do not use the "<<" operator for PasswordForms, because it also prints | |
69 // passwords. Also, that operator is only for testing. | |
70 log.SetString("scheme", FormSchemeToString(form.scheme)); | |
71 log.SetString("signon realm", ScrubURL(GURL(form.signon_realm))); | |
72 log.SetString("original signon realm", | |
73 ScrubURL(GURL(form.original_signon_realm))); | |
74 log.SetString("origin", ScrubURL(form.origin)); | |
75 log.SetString("action", ScrubURL(form.action)); | |
76 log.SetString("username element", form.username_element); | |
77 log.SetString("password element", form.password_element); | |
78 log.SetBoolean("password autocomplete set", form.password_autocomplete_set); | |
79 log.SetString("old password element", form.old_password_element); | |
80 log.SetBoolean("ssl valid", form.ssl_valid); | |
81 log.SetBoolean("password generated", | |
82 form.type == PasswordForm::TYPE_GENERATED); | |
83 log.SetInteger("times used", form.times_used); | |
84 log.SetBoolean("use additional authentication", | |
85 form.use_additional_authentication); | |
86 log.SetBoolean("is PSL match", form.IsPublicSuffixMatch()); | |
87 LogValue(message, log); | |
88 } | 164 } |
89 | 165 |
90 void SavePasswordProgressLogger::LogHTMLForm(const std::string& message, | 166 SavePasswordProgressLogger::~SavePasswordProgressLogger() { |
91 const std::string& name_or_id, | |
92 const std::string& method, | |
93 const GURL& action) { | |
94 DictionaryValue log; | |
95 log.SetString("name_or_id", name_or_id); | |
96 log.SetString("method", method); | |
97 log.SetString("action", ScrubURL(action)); | |
98 LogValue(message, log); | |
99 } | 167 } |
100 | 168 |
101 void SavePasswordProgressLogger::LogURL(const std::string& message, | 169 void SavePasswordProgressLogger::LogPasswordForm( |
102 const GURL& url) { | 170 SavePasswordProgressLogger::StringID label, |
103 LogValue(message, StringValue(ScrubURL(url))); | 171 const PasswordForm& form) { |
| 172 DictionaryValue log; |
| 173 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), |
| 174 FormSchemeToString(form.scheme)); |
| 175 log.SetString(GetStringFromID(STRING_SCHEME_MESSAGE), |
| 176 FormSchemeToString(form.scheme)); |
| 177 log.SetString(GetStringFromID(STRING_SIGNON_REALM), |
| 178 ScrubURL(GURL(form.signon_realm))); |
| 179 log.SetString(GetStringFromID(STRING_ORIGINAL_SIGNON_REALM), |
| 180 ScrubURL(GURL(form.original_signon_realm))); |
| 181 log.SetString(GetStringFromID(STRING_ORIGIN), ScrubURL(form.origin)); |
| 182 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(form.action)); |
| 183 log.SetString(GetStringFromID(STRING_USERNAME_ELEMENT), |
| 184 ScrubElementID(form.username_element)); |
| 185 log.SetString(GetStringFromID(STRING_PASSWORD_ELEMENT), |
| 186 ScrubElementID(form.password_element)); |
| 187 log.SetBoolean(GetStringFromID(STRING_PASSWORD_AUTOCOMPLETE_SET), |
| 188 form.password_autocomplete_set); |
| 189 log.SetString(GetStringFromID(STRING_OLD_PASSWORD_ELEMENT), |
| 190 ScrubElementID(form.old_password_element)); |
| 191 log.SetBoolean(GetStringFromID(STRING_SSL_VALID), form.ssl_valid); |
| 192 log.SetBoolean(GetStringFromID(STRING_PASSWORD_GENERATED), |
| 193 form.type == PasswordForm::TYPE_GENERATED); |
| 194 log.SetInteger(GetStringFromID(STRING_TIMES_USED), form.times_used); |
| 195 log.SetBoolean(GetStringFromID(STRING_USE_ADDITIONAL_AUTHENTICATION), |
| 196 form.use_additional_authentication); |
| 197 log.SetBoolean(GetStringFromID(STRING_PSL_MATCH), form.IsPublicSuffixMatch()); |
| 198 LogValue(label, log); |
104 } | 199 } |
105 | 200 |
106 void SavePasswordProgressLogger::LogBoolean(const std::string& message, | 201 void SavePasswordProgressLogger::LogHTMLForm( |
107 bool value) { | 202 SavePasswordProgressLogger::StringID label, |
108 LogValue(message, FundamentalValue(value)); | 203 const std::string& name_or_id, |
| 204 const std::string& method, |
| 205 const GURL& action) { |
| 206 DictionaryValue log; |
| 207 log.SetString(GetStringFromID(STRING_NAME_OR_ID), ScrubElementID(name_or_id)); |
| 208 log.SetString(GetStringFromID(STRING_METHOD), FormMethodToString(method)); |
| 209 log.SetString(GetStringFromID(STRING_ACTION), ScrubURL(action)); |
| 210 LogValue(label, log); |
109 } | 211 } |
110 | 212 |
111 void SavePasswordProgressLogger::LogNumber(const std::string& message, | 213 void SavePasswordProgressLogger::LogURL( |
112 int value) { | 214 SavePasswordProgressLogger::StringID label, |
113 LogValue(message, FundamentalValue(value)); | 215 const GURL& url) { |
| 216 LogValue(label, StringValue(ScrubURL(url))); |
114 } | 217 } |
115 | 218 |
116 void SavePasswordProgressLogger::LogNumber(const std::string& message, | 219 void SavePasswordProgressLogger::LogBoolean( |
117 size_t value) { | 220 SavePasswordProgressLogger::StringID label, |
118 LogValue(message, FundamentalValue(checked_cast<int, size_t>(value))); | 221 bool truth_value) { |
| 222 LogValue(label, FundamentalValue(truth_value)); |
119 } | 223 } |
120 | 224 |
121 void SavePasswordProgressLogger::LogFinalDecision(Decision decision) { | 225 void SavePasswordProgressLogger::LogNumber( |
122 LogValue("Final decision taken", DecisionToStringValue(decision)); | 226 SavePasswordProgressLogger::StringID label, |
| 227 int signed_number) { |
| 228 LogValue(label, FundamentalValue(signed_number)); |
123 } | 229 } |
124 | 230 |
125 void SavePasswordProgressLogger::LogMessage(const std::string& message) { | 231 void SavePasswordProgressLogger::LogNumber( |
126 LogValue("Message", StringValue(message)); | 232 SavePasswordProgressLogger::StringID label, |
| 233 size_t unsigned_number) { |
| 234 int signed_number = checked_cast<int, size_t>(unsigned_number); |
| 235 LogNumber(label, signed_number); |
127 } | 236 } |
128 | 237 |
129 void SavePasswordProgressLogger::LogValue(const std::string& name, | 238 void SavePasswordProgressLogger::LogMessage( |
130 const Value& log) { | 239 SavePasswordProgressLogger::StringID message) { |
| 240 LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message))); |
| 241 } |
| 242 |
| 243 void SavePasswordProgressLogger::LogValue(StringID label, const Value& log) { |
131 std::string log_string; | 244 std::string log_string; |
132 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( | 245 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( |
133 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); | 246 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); |
134 DCHECK(conversion_to_string_successful); | 247 DCHECK(conversion_to_string_successful); |
135 SendLog(name + ": " + log_string); | 248 SendLog(GetStringFromID(label) + ": " + log_string); |
136 } | 249 } |
137 | 250 |
138 } // namespace autofill | 251 } // namespace autofill |
OLD | NEW |