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" |
11 #include "base/memory/scoped_ptr.h" | |
9 #include "base/numerics/safe_conversions.h" | 12 #include "base/numerics/safe_conversions.h" |
13 #include "base/strings/string16.h" | |
14 #include "base/strings/string_util.h" | |
15 #include "base/strings/utf_string_conversions.h" | |
10 #include "base/values.h" | 16 #include "base/values.h" |
11 #include "components/autofill/core/common/password_form.h" | 17 #include "components/autofill/core/common/password_form.h" |
12 | 18 |
13 using base::checked_cast; | 19 using base::checked_cast; |
14 using base::Value; | 20 using base::Value; |
15 using base::DictionaryValue; | 21 using base::DictionaryValue; |
16 using base::FundamentalValue; | 22 using base::FundamentalValue; |
17 using base::StringValue; | 23 using base::StringValue; |
18 | 24 |
19 namespace autofill { | 25 namespace autofill { |
20 | 26 |
21 namespace { | 27 namespace { |
22 | 28 |
29 // Note 1: Caching the ID->string map in an array would be probably faster, but | |
30 // the switch statement is (a) robust against re-ordering, and (b) checks in | |
31 // compile-time, that all IDs get a string assigned. The expected frequency of | |
32 // calls is low enough (in particular, zero if password manager internals page | |
33 // is not open), that optimizing for code robustness is preferred against speed. | |
34 // Note 2: The messages can be used as dictionary keys. Do not use '.' in them. | |
35 std::string GetStringFromID(SavePasswordProgressLogger::StringID id) { | |
36 switch (id) { | |
37 case SavePasswordProgressLogger::STRING_DECISION_ASK: | |
38 return "Decision: ASK the user"; | |
39 case SavePasswordProgressLogger::STRING_DECISION_DROP: | |
40 return "Decision: DROP the password"; | |
41 case SavePasswordProgressLogger::STRING_DECISION_SAVE: | |
42 return "Decision: SAVE the password"; | |
43 case SavePasswordProgressLogger::STRING_METHOD: | |
44 return "Form method"; | |
45 case SavePasswordProgressLogger::STRING_METHOD_GET: | |
46 return "GET"; | |
47 case SavePasswordProgressLogger::STRING_METHOD_POST: | |
48 return "POST"; | |
49 case SavePasswordProgressLogger::STRING_METHOD_EMPTY: | |
50 return "(empty)"; | |
51 case SavePasswordProgressLogger::STRING_OTHER: | |
52 return "(other)"; | |
53 case SavePasswordProgressLogger::STRING_SCHEME_HTML: | |
54 return "HTML"; | |
55 case SavePasswordProgressLogger::STRING_SCHEME_BASIC: | |
56 return "Basic"; | |
57 case SavePasswordProgressLogger::STRING_SCHEME_DIGEST: | |
58 return "Digest"; | |
59 case SavePasswordProgressLogger::STRING_SCHEME_MESSAGE: | |
60 return "Scheme"; | |
61 case SavePasswordProgressLogger::STRING_SIGNON_REALM: | |
62 return "Signon realm"; | |
63 case SavePasswordProgressLogger::STRING_ORIGINAL_SIGNON_REALM: | |
64 return "Original signon realm"; | |
65 case SavePasswordProgressLogger::STRING_ORIGIN: | |
66 return "Origin"; | |
67 case SavePasswordProgressLogger::STRING_ACTION: | |
68 return "Action"; | |
69 case SavePasswordProgressLogger::STRING_USERNAME_ELEMENT: | |
70 return "Username element"; | |
71 case SavePasswordProgressLogger::STRING_PASSWORD_ELEMENT: | |
72 return "Password element"; | |
73 case SavePasswordProgressLogger::STRING_PASSWORD_AUTOCOMPLETE_SET: | |
74 return "Password autocomplete set"; | |
75 case SavePasswordProgressLogger::STRING_OLD_PASSWORD_ELEMENT: | |
76 return "Old password element"; | |
77 case SavePasswordProgressLogger::STRING_SSL_VALID: | |
78 return "SSL valid"; | |
79 case SavePasswordProgressLogger::STRING_PASSWORD_GENERATED: | |
80 return "Password generated"; | |
81 case SavePasswordProgressLogger::STRING_TIMES_USED: | |
82 return "Times used"; | |
83 case SavePasswordProgressLogger::STRING_USE_ADDITIONAL_AUTHENTICATION: | |
84 return "Use additional authentication"; | |
85 case SavePasswordProgressLogger::STRING_PSL_MATCH: | |
86 return "PSL match"; | |
87 case SavePasswordProgressLogger::STRING_NAME_OR_ID: | |
88 return "Form name or ID"; | |
89 case SavePasswordProgressLogger::STRING_MESSAGE: | |
90 return "Message"; | |
91 case SavePasswordProgressLogger::STRING_INVALID: | |
92 return "INVALID"; | |
93 // Intentionally no default: clause here -- all IDs need to get covered. | |
94 } | |
95 NOTREACHED(); // Win compilers don't believe this is unreachable. | |
96 return std::string(); | |
97 }; | |
98 | |
23 // Removes privacy sensitive parts of |url| (currently all but host and scheme). | 99 // Removes privacy sensitive parts of |url| (currently all but host and scheme). |
24 std::string ScrubURL(const GURL& url) { | 100 std::string ScrubURL(const GURL& url) { |
25 if (url.is_valid()) | 101 if (url.is_valid()) |
26 return url.GetWithEmptyPath().spec(); | 102 return url.GetWithEmptyPath().spec(); |
27 return std::string(); | 103 return std::string(); |
28 } | 104 } |
29 | 105 |
30 std::string FormSchemeToString(PasswordForm::Scheme scheme) { | 106 // Returns true for all characters which we don't want to see in the logged IDs |
107 // or names of HTML elements. | |
108 bool IsUnwantedInElementID(char c) { | |
109 return !(c == '_' || c == '-' || IsAsciiAlpha(c) || IsAsciiDigit(c)); | |
110 } | |
111 | |
112 // Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns | |
113 // all characters to lowercase. This damages some valid HTML element IDs or | |
114 // names, but it is likely that it will be still possible to match the scrubbed | |
115 // string to the original ID or name in the HTML doc. That's good enough for the | |
116 // logging purposes, and provides some security benefits. | |
117 std::string ScrubElementID(std::string element_id) { | |
118 std::replace_if( | |
119 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); | |
120 return StringToLowerASCII(element_id); | |
121 } | |
122 | |
123 SavePasswordProgressLogger::StringID FormSchemeToStringID( | |
124 PasswordForm::Scheme scheme) { | |
31 switch (scheme) { | 125 switch (scheme) { |
32 case PasswordForm::SCHEME_HTML: | 126 case PasswordForm::SCHEME_HTML: |
33 return "HTML"; | 127 return SavePasswordProgressLogger::STRING_SCHEME_HTML; |
34 case PasswordForm::SCHEME_BASIC: | 128 case PasswordForm::SCHEME_BASIC: |
35 return "BASIC"; | 129 return SavePasswordProgressLogger::STRING_SCHEME_BASIC; |
36 case PasswordForm::SCHEME_DIGEST: | 130 case PasswordForm::SCHEME_DIGEST: |
37 return "DIGEST"; | 131 return SavePasswordProgressLogger::STRING_SCHEME_DIGEST; |
38 case PasswordForm::SCHEME_OTHER: | 132 case PasswordForm::SCHEME_OTHER: |
39 return "OTHER"; | 133 return SavePasswordProgressLogger::STRING_OTHER; |
40 } | 134 } |
41 NOTREACHED(); // Win compilers don't believe this is unreachable. | 135 NOTREACHED(); // Win compilers don't believe this is unreachable. |
42 return std::string(); | 136 return SavePasswordProgressLogger::STRING_INVALID; |
43 } | 137 } |
44 | 138 |
45 StringValue DecisionToStringValue( | 139 SavePasswordProgressLogger::StringID FormMethodToStringID( |
46 SavePasswordProgressLogger::Decision decision) { | 140 const std::string& method) { |
47 switch (decision) { | 141 std::string method_processed; |
48 case SavePasswordProgressLogger::DECISION_SAVE: | 142 base::TrimWhitespaceASCII( |
49 return StringValue("SAVE the password"); | 143 StringToLowerASCII(method), base::TRIM_ALL, &method_processed); |
50 case SavePasswordProgressLogger::DECISION_ASK: | 144 if (method_processed.empty()) |
51 return StringValue("ASK the user whether to save the password"); | 145 return SavePasswordProgressLogger::STRING_METHOD_EMPTY; |
52 case SavePasswordProgressLogger::DECISION_DROP: | 146 else if (method_processed == "get") |
53 return StringValue("DROP the password"); | 147 return SavePasswordProgressLogger::STRING_METHOD_GET; |
54 } | 148 else if (method_processed == "post") |
55 NOTREACHED(); // Win compilers don't believe this is unreachable. | 149 return SavePasswordProgressLogger::STRING_METHOD_POST; |
56 return StringValue(std::string()); | 150 else |
151 return SavePasswordProgressLogger::STRING_OTHER; | |
152 } | |
153 | |
154 // GetValueFrom*: Sanitize the arguments, and return them as base::Value. | |
155 scoped_ptr<Value> GetValueFromURL(const GURL& url) { | |
156 return scoped_ptr<Value>(Value::CreateStringValue(ScrubURL(url))); | |
157 } | |
158 | |
159 scoped_ptr<Value> GetValueFromMessage( | |
160 SavePasswordProgressLogger::StringID message) { | |
161 return scoped_ptr<Value>(Value::CreateStringValue(GetStringFromID(message))); | |
162 } | |
163 | |
164 scoped_ptr<Value> GetValueFromNumber(int number) { | |
165 return scoped_ptr<Value>(Value::CreateIntegerValue(number)); | |
166 } | |
167 | |
168 scoped_ptr<Value> GetValueFromBoolean(bool truth_value) { | |
169 return scoped_ptr<Value>(Value::CreateBooleanValue(truth_value)); | |
170 } | |
171 | |
172 scoped_ptr<Value> GetValueFromElementID(const std::string& element_id) { | |
173 return scoped_ptr<Value>( | |
174 Value::CreateStringValue(ScrubElementID(element_id))); | |
175 } | |
176 | |
177 void AddURLToDict(DictionaryValue* dict, | |
178 SavePasswordProgressLogger::StringID label, | |
179 const GURL& url) { | |
180 dict->Set(GetStringFromID(label), GetValueFromURL(url).release()); | |
181 } | |
182 | |
183 void AddMessageToDict(DictionaryValue* dict, | |
184 SavePasswordProgressLogger::StringID label, | |
185 SavePasswordProgressLogger::StringID message) { | |
186 dict->Set(GetStringFromID(label), GetValueFromMessage(message).release()); | |
187 } | |
188 | |
189 void AddNumberToDict(DictionaryValue* dict, | |
190 SavePasswordProgressLogger::StringID label, | |
191 int number) { | |
192 dict->Set(GetStringFromID(label), GetValueFromNumber(number).release()); | |
193 } | |
194 | |
195 void AddBooleanToDict(DictionaryValue* dict, | |
196 SavePasswordProgressLogger::StringID label, | |
197 bool truth_value) { | |
198 dict->Set(GetStringFromID(label), GetValueFromBoolean(truth_value).release()); | |
199 } | |
200 | |
201 void AddElementIDToDict(DictionaryValue* dict, | |
202 SavePasswordProgressLogger::StringID label, | |
203 const std::string& element_id) { | |
204 dict->Set(GetStringFromID(label), | |
205 GetValueFromElementID(element_id).release()); | |
206 } | |
207 | |
208 void AddElementIDToDict(DictionaryValue* dict, | |
209 SavePasswordProgressLogger::StringID label, | |
210 const base::string16& element_id) { | |
211 dict->Set(GetStringFromID(label), | |
212 GetValueFromElementID(base::UTF16ToUTF8(element_id)).release()); | |
57 } | 213 } |
58 | 214 |
59 } // namespace | 215 } // namespace |
60 | 216 |
61 SavePasswordProgressLogger::SavePasswordProgressLogger() {} | 217 SavePasswordProgressLogger::SavePasswordProgressLogger() { |
62 | 218 } |
63 SavePasswordProgressLogger::~SavePasswordProgressLogger() {} | 219 |
64 | 220 SavePasswordProgressLogger::~SavePasswordProgressLogger() { |
65 void SavePasswordProgressLogger::LogPasswordForm(const std::string& message, | 221 } |
66 const PasswordForm& form) { | 222 |
223 void SavePasswordProgressLogger::LogPasswordForm( | |
224 SavePasswordProgressLogger::StringID label, | |
225 const PasswordForm& form) { | |
67 DictionaryValue log; | 226 DictionaryValue log; |
68 // Do not use the "<<" operator for PasswordForms, because it also prints | 227 AddMessageToDict( |
69 // passwords. Also, that operator is only for testing. | 228 &log, STRING_SCHEME_MESSAGE, FormSchemeToStringID(form.scheme)); |
Ilya Sherman
2014/04/17 21:12:05
Comparing
AddMessageToDict(
&log, STRING_SCHE
vabr (Chromium)
2014/04/19 07:39:53
It was, in fact, very convincing.
Happy to remove
| |
70 log.SetString("scheme", FormSchemeToString(form.scheme)); | 229 AddMessageToDict( |
71 log.SetString("signon realm", ScrubURL(GURL(form.signon_realm))); | 230 &log, STRING_SCHEME_MESSAGE, FormSchemeToStringID(form.scheme)); |
72 log.SetString("original signon realm", | 231 AddURLToDict(&log, STRING_SIGNON_REALM, GURL(form.signon_realm)); |
73 ScrubURL(GURL(form.original_signon_realm))); | 232 AddURLToDict( |
74 log.SetString("origin", ScrubURL(form.origin)); | 233 &log, STRING_ORIGINAL_SIGNON_REALM, GURL(form.original_signon_realm)); |
75 log.SetString("action", ScrubURL(form.action)); | 234 AddURLToDict(&log, STRING_ORIGIN, form.origin); |
76 log.SetString("username element", form.username_element); | 235 AddURLToDict(&log, STRING_ACTION, form.action); |
77 log.SetString("password element", form.password_element); | 236 AddElementIDToDict(&log, STRING_USERNAME_ELEMENT, form.username_element); |
78 log.SetBoolean("password autocomplete set", form.password_autocomplete_set); | 237 AddElementIDToDict(&log, STRING_PASSWORD_ELEMENT, form.password_element); |
79 log.SetString("old password element", form.old_password_element); | 238 AddBooleanToDict( |
80 log.SetBoolean("ssl valid", form.ssl_valid); | 239 &log, STRING_PASSWORD_AUTOCOMPLETE_SET, form.password_autocomplete_set); |
81 log.SetBoolean("password generated", | 240 AddElementIDToDict( |
82 form.type == PasswordForm::TYPE_GENERATED); | 241 &log, STRING_OLD_PASSWORD_ELEMENT, form.old_password_element); |
83 log.SetInteger("times used", form.times_used); | 242 AddBooleanToDict(&log, STRING_SSL_VALID, form.ssl_valid); |
84 log.SetBoolean("use additional authentication", | 243 AddBooleanToDict(&log, |
85 form.use_additional_authentication); | 244 STRING_PASSWORD_GENERATED, |
86 log.SetBoolean("is PSL match", form.IsPublicSuffixMatch()); | 245 form.type == PasswordForm::TYPE_GENERATED); |
87 LogValue(message, log); | 246 AddNumberToDict(&log, STRING_TIMES_USED, form.times_used); |
88 } | 247 AddBooleanToDict(&log, |
89 | 248 STRING_USE_ADDITIONAL_AUTHENTICATION, |
90 void SavePasswordProgressLogger::LogHTMLForm(const std::string& message, | 249 form.use_additional_authentication); |
91 const std::string& name_or_id, | 250 AddBooleanToDict(&log, STRING_PSL_MATCH, form.IsPublicSuffixMatch()); |
92 const std::string& method, | 251 LogValue(label, &log); |
93 const GURL& action) { | 252 } |
253 | |
254 void SavePasswordProgressLogger::LogHTMLForm( | |
255 SavePasswordProgressLogger::StringID label, | |
256 const std::string& name_or_id, | |
257 const std::string& method, | |
258 const GURL& action) { | |
94 DictionaryValue log; | 259 DictionaryValue log; |
95 log.SetString("name_or_id", name_or_id); | 260 AddElementIDToDict(&log, STRING_NAME_OR_ID, name_or_id); |
96 log.SetString("method", method); | 261 AddMessageToDict(&log, STRING_METHOD, FormMethodToStringID(method)); |
97 log.SetString("action", ScrubURL(action)); | 262 AddURLToDict(&log, STRING_ACTION, action); |
98 LogValue(message, log); | 263 LogValue(label, &log); |
99 } | 264 } |
100 | 265 |
101 void SavePasswordProgressLogger::LogURL(const std::string& message, | 266 void SavePasswordProgressLogger::LogURL( |
102 const GURL& url) { | 267 SavePasswordProgressLogger::StringID label, |
103 LogValue(message, StringValue(ScrubURL(url))); | 268 const GURL& url) { |
104 } | 269 LogValue(label, GetValueFromURL(url).get()); |
105 | 270 } |
106 void SavePasswordProgressLogger::LogBoolean(const std::string& message, | 271 |
107 bool value) { | 272 void SavePasswordProgressLogger::LogBoolean( |
108 LogValue(message, FundamentalValue(value)); | 273 SavePasswordProgressLogger::StringID label, |
109 } | 274 bool truth_value) { |
110 | 275 LogValue(label, GetValueFromBoolean(truth_value).get()); |
111 void SavePasswordProgressLogger::LogNumber(const std::string& message, | 276 } |
112 int value) { | 277 |
113 LogValue(message, FundamentalValue(value)); | 278 void SavePasswordProgressLogger::LogNumber( |
114 } | 279 SavePasswordProgressLogger::StringID label, |
115 | 280 int signed_number) { |
116 void SavePasswordProgressLogger::LogNumber(const std::string& message, | 281 LogValue(label, GetValueFromNumber(signed_number).get()); |
117 size_t value) { | 282 } |
118 LogValue(message, FundamentalValue(checked_cast<int, size_t>(value))); | 283 |
119 } | 284 void SavePasswordProgressLogger::LogNumber( |
120 | 285 SavePasswordProgressLogger::StringID label, |
121 void SavePasswordProgressLogger::LogFinalDecision(Decision decision) { | 286 size_t unsigned_number) { |
122 LogValue("Final decision taken", DecisionToStringValue(decision)); | 287 LogValue( |
123 } | 288 label, |
124 | 289 GetValueFromNumber(checked_cast<int, size_t>(unsigned_number)).get()); |
125 void SavePasswordProgressLogger::LogMessage(const std::string& message) { | 290 } |
126 LogValue("Message", StringValue(message)); | 291 |
127 } | 292 void SavePasswordProgressLogger::LogMessage( |
128 | 293 SavePasswordProgressLogger::StringID message) { |
129 void SavePasswordProgressLogger::LogValue(const std::string& name, | 294 LogValue(STRING_MESSAGE, GetValueFromMessage(message).get()); |
130 const Value& log) { | 295 } |
296 | |
297 void SavePasswordProgressLogger::LogValue(StringID label, const Value* log) { | |
131 std::string log_string; | 298 std::string log_string; |
132 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( | 299 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( |
133 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); | 300 log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); |
134 DCHECK(conversion_to_string_successful); | 301 DCHECK(conversion_to_string_successful); |
135 SendLog(name + ": " + log_string); | 302 SendLog(GetStringFromID(label) + ": " + log_string); |
136 } | 303 } |
137 | 304 |
138 } // namespace autofill | 305 } // namespace autofill |
OLD | NEW |