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/macros.h" |
| 12 #include "base/memory/scoped_ptr.h" |
9 #include "base/numerics/safe_conversions.h" | 13 #include "base/numerics/safe_conversions.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 // These messages can be used as dictionary keys. Do not use '.' in them. |
| 30 const char* const kIDsToMessages[] = { |
| 31 "Decision: ASK the user", // STRING_DECISION_ASK |
| 32 "Decision: DROP the password", // STRING_DECISION_DROP, |
| 33 "Decision: SAVE the password", // STRING_DECISION_SAVE, |
| 34 "Form method", // STRING_METHOD, |
| 35 "GET", // STRING_METHOD_GET, |
| 36 "POST", // STRING_METHOD_POST, |
| 37 "(empty)", // STRING_METHOD_EMPTY, |
| 38 "(other)", // STRING_OTHER, |
| 39 "HTML", // STRING_SCHEME_HTML, |
| 40 "Basic", // STRING_SCHEME_BASIC, |
| 41 "Digest", // STRING_SCHEME_DIGEST, |
| 42 "Scheme", // STRING_SCHEME_MESSAGE, |
| 43 "Signon realm", // STRING_SIGNON_REALM, |
| 44 "Original signon realm", // STRING_ORIGINAL_SIGNON_REALM, |
| 45 "Origin", // STRING_ORIGIN, |
| 46 "Action", // STRING_ACTION, |
| 47 "Username element", // STRING_USERNAME_ELEMENT, |
| 48 "Password element", // STRING_PASSWORD_ELEMENT, |
| 49 "Password autocomplete set", // STRING_PASSWORD_AUTOCOMPLETE_SET, |
| 50 "Old password element", // STRING_OLD_PASSWORD_ELEMENT, |
| 51 "SSL valid", // STRING_SSL_VALID, |
| 52 "Password generated", // STRING_PASSWORD_GENERATED, |
| 53 "Times used", // STRING_TIMES_USED, |
| 54 "Use additional authentication", // STRING_USE_ADDITIONAL_AUTHENTICATION, |
| 55 "PSL match", // STRING_PSL_MATCH, |
| 56 "Form name or ID", // STRING_NAME_OR_ID, |
| 57 "Message", // STRING_MESSAGE, |
| 58 "INVALID", // STRING_INVALID, |
| 59 }; |
| 60 |
| 61 COMPILE_ASSERT(SavePasswordProgressLogger::StringID::STRING_MAX + 1 == |
| 62 arraysize(kIDsToMessages), |
| 63 IDsToMessagesMappingIsOutOfDate); |
| 64 |
23 // Removes privacy sensitive parts of |url| (currently all but host and scheme). | 65 // Removes privacy sensitive parts of |url| (currently all but host and scheme). |
24 std::string ScrubURL(const GURL& url) { | 66 std::string ScrubURL(const GURL& url) { |
25 if (url.is_valid()) | 67 if (url.is_valid()) |
26 return url.GetWithEmptyPath().spec(); | 68 return url.GetWithEmptyPath().spec(); |
27 return std::string(); | 69 return std::string(); |
28 } | 70 } |
29 | 71 |
30 std::string FormSchemeToString(PasswordForm::Scheme scheme) { | 72 // Returns true for all characters which we don't want to see in the logged IDs |
| 73 // or names of HTML elements. |
| 74 bool IsUnwantedInElementID (char c) { |
| 75 return !IsAsciiAlpha(c) && !IsAsciiDigit(c); |
| 76 } |
| 77 |
| 78 // Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns |
| 79 // all characters to lowercase. This damages some valid HTML element IDs or |
| 80 // names, but it is likely that it will be still possible to match the scrubbed |
| 81 // string to the original ID or name in the HTML doc. That's good enough for the |
| 82 // logging purposes, and provides some security benefits. |
| 83 std::string ScrubElementID(std::string element_id) { |
| 84 std::replace_if( |
| 85 element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); |
| 86 return StringToLowerASCII(element_id); |
| 87 } |
| 88 |
| 89 SavePasswordProgressLogger::StringID FormSchemeToStringID( |
| 90 PasswordForm::Scheme scheme) { |
31 switch (scheme) { | 91 switch (scheme) { |
32 case PasswordForm::SCHEME_HTML: | 92 case PasswordForm::SCHEME_HTML: |
33 return "HTML"; | 93 return SavePasswordProgressLogger::STRING_SCHEME_HTML; |
34 case PasswordForm::SCHEME_BASIC: | 94 case PasswordForm::SCHEME_BASIC: |
35 return "BASIC"; | 95 return SavePasswordProgressLogger::STRING_SCHEME_BASIC; |
36 case PasswordForm::SCHEME_DIGEST: | 96 case PasswordForm::SCHEME_DIGEST: |
37 return "DIGEST"; | 97 return SavePasswordProgressLogger::STRING_SCHEME_DIGEST; |
38 case PasswordForm::SCHEME_OTHER: | 98 case PasswordForm::SCHEME_OTHER: |
39 return "OTHER"; | 99 return SavePasswordProgressLogger::STRING_OTHER; |
40 } | 100 } |
41 NOTREACHED(); // Win compilers don't believe this is unreachable. | 101 NOTREACHED(); // Win compilers don't believe this is unreachable. |
42 return std::string(); | 102 return SavePasswordProgressLogger::StringID::STRING_INVALID; |
43 } | 103 } |
44 | 104 |
45 StringValue DecisionToStringValue( | 105 SavePasswordProgressLogger::StringID FormMethodToStringID( |
46 SavePasswordProgressLogger::Decision decision) { | 106 const std::string& method) { |
47 switch (decision) { | 107 std::string method_processed; |
48 case SavePasswordProgressLogger::DECISION_SAVE: | 108 base::TrimWhitespaceASCII( |
49 return StringValue("SAVE the password"); | 109 StringToLowerASCII(method), base::TRIM_ALL, &method_processed); |
50 case SavePasswordProgressLogger::DECISION_ASK: | 110 if (method_processed.empty()) |
51 return StringValue("ASK the user whether to save the password"); | 111 return SavePasswordProgressLogger::STRING_METHOD_EMPTY; |
52 case SavePasswordProgressLogger::DECISION_DROP: | 112 else if (method_processed == "get") |
53 return StringValue("DROP the password"); | 113 return SavePasswordProgressLogger::STRING_METHOD_GET; |
| 114 else if (method_processed == "post") |
| 115 return SavePasswordProgressLogger::STRING_METHOD_POST; |
| 116 else |
| 117 return SavePasswordProgressLogger::STRING_OTHER; |
| 118 } |
| 119 |
| 120 // Interprets |log|, sanitizes its value if needed, and returns it as |
| 121 // base::Value. Does not work on LOG_TYPE_LABEL logs. |
| 122 scoped_ptr<Value> GetValueFromStructuredLog( |
| 123 const SavePasswordProgressLogger::StructuredLog& log) { |
| 124 switch (log.log_type) { |
| 125 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_LABEL: |
| 126 NOTREACHED(); |
| 127 return scoped_ptr<Value>(); |
| 128 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_GURL: |
| 129 return scoped_ptr<Value>(Value::CreateStringValue(ScrubURL(log.url))); |
| 130 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_MESSAGE: |
| 131 return scoped_ptr<Value>( |
| 132 Value::CreateStringValue(kIDsToMessages[log.message])); |
| 133 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_NUMBER: |
| 134 return scoped_ptr<Value>(Value::CreateIntegerValue(log.number)); |
| 135 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_BOOL: |
| 136 return scoped_ptr<Value>(Value::CreateBooleanValue(log.truth_value)); |
| 137 case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_ELEMENT_ID: |
| 138 return scoped_ptr<Value>( |
| 139 Value::CreateStringValue(ScrubElementID(log.element_id))); |
54 } | 140 } |
55 NOTREACHED(); // Win compilers don't believe this is unreachable. | 141 NOTREACHED(); // Win compilers don't believe this is unreachable. |
56 return StringValue(std::string()); | 142 return scoped_ptr<Value>(); |
57 } | 143 } |
58 | 144 |
59 } // namespace | 145 } // namespace |
60 | 146 |
| 147 SavePasswordProgressLogger::StructuredLog::StructuredLog() |
| 148 : log_type(LOG_TYPE_LABEL), |
| 149 label(STRING_INVALID), |
| 150 message(STRING_INVALID), |
| 151 number(0), |
| 152 truth_value(false) { |
| 153 } |
| 154 |
| 155 SavePasswordProgressLogger::StructuredLog::StructuredLog( |
| 156 SavePasswordProgressLogger::StringID label) |
| 157 : log_type(LOG_TYPE_LABEL), |
| 158 label(label), |
| 159 message(STRING_INVALID), |
| 160 number(0), |
| 161 truth_value(false) { |
| 162 } |
| 163 |
| 164 SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
| 165 GURL url) |
| 166 : log_type(LOG_TYPE_GURL), |
| 167 label(label), |
| 168 url(url), |
| 169 message(STRING_INVALID), |
| 170 number(0), |
| 171 truth_value(false) { |
| 172 } |
| 173 |
| 174 SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
| 175 StringID message) |
| 176 : log_type(LOG_TYPE_MESSAGE), |
| 177 label(label), |
| 178 message(message), |
| 179 number(0), |
| 180 truth_value(false) { |
| 181 } |
| 182 |
| 183 SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
| 184 int number) |
| 185 : log_type(LOG_TYPE_NUMBER), |
| 186 label(label), |
| 187 message(STRING_INVALID), |
| 188 number(number), |
| 189 truth_value(false) { |
| 190 } |
| 191 |
| 192 SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
| 193 bool truth_value) |
| 194 : log_type(LOG_TYPE_BOOL), |
| 195 label(label), |
| 196 message(STRING_INVALID), |
| 197 number(0), |
| 198 truth_value(truth_value) { |
| 199 } |
| 200 |
| 201 SavePasswordProgressLogger::StructuredLog::StructuredLog( |
| 202 StringID label, |
| 203 const base::string16& element_id) |
| 204 : log_type(LOG_TYPE_ELEMENT_ID), |
| 205 label(label), |
| 206 message(STRING_INVALID), |
| 207 number(0), |
| 208 truth_value(false), |
| 209 element_id(UTF16ToUTF8(element_id)) { |
| 210 } |
| 211 |
| 212 SavePasswordProgressLogger::StructuredLog::StructuredLog( |
| 213 StringID label, |
| 214 const std::string& element_id) |
| 215 : log_type(LOG_TYPE_ELEMENT_ID), |
| 216 label(label), |
| 217 message(STRING_INVALID), |
| 218 number(0), |
| 219 truth_value(false), |
| 220 element_id(element_id) { |
| 221 } |
| 222 |
| 223 SavePasswordProgressLogger::StructuredLog::~StructuredLog() { |
| 224 } |
| 225 |
| 226 bool SavePasswordProgressLogger::StructuredLog::operator==( |
| 227 const SavePasswordProgressLogger::StructuredLog& log) const { |
| 228 if (log_type != log.log_type || label != log.label) |
| 229 return false; |
| 230 switch (log_type) { |
| 231 case LOG_TYPE_LABEL: |
| 232 return true; |
| 233 case LOG_TYPE_GURL: |
| 234 return url == log.url; |
| 235 case LOG_TYPE_MESSAGE: |
| 236 return message == log.message; |
| 237 case LOG_TYPE_NUMBER: |
| 238 return number == log.number; |
| 239 case LOG_TYPE_BOOL: |
| 240 return truth_value == log.truth_value; |
| 241 case LOG_TYPE_ELEMENT_ID: |
| 242 return element_id == log.element_id; |
| 243 }; |
| 244 NOTREACHED(); // Win compilers don't believe this is unreachable. |
| 245 return false; |
| 246 } |
| 247 |
| 248 bool SavePasswordProgressLogger::StructuredLog::operator!=( |
| 249 const SavePasswordProgressLogger::StructuredLog& log) const { |
| 250 return !operator==(log); |
| 251 } |
| 252 |
| 253 |
61 SavePasswordProgressLogger::SavePasswordProgressLogger() {} | 254 SavePasswordProgressLogger::SavePasswordProgressLogger() {} |
62 | 255 |
63 SavePasswordProgressLogger::~SavePasswordProgressLogger() {} | 256 SavePasswordProgressLogger::~SavePasswordProgressLogger() {} |
64 | 257 |
65 void SavePasswordProgressLogger::LogPasswordForm(const std::string& message, | 258 // static |
66 const PasswordForm& form) { | 259 std::string SavePasswordProgressLogger::SanitizeStructuredLogs( |
67 DictionaryValue log; | 260 const std::vector<SavePasswordProgressLogger::StructuredLog>& logs) { |
68 // Do not use the "<<" operator for PasswordForms, because it also prints | 261 // First, we sanitize the logs and transform them into a base::Value. |
69 // passwords. Also, that operator is only for testing. | 262 DictionaryValue log_dict; // Always holds a single item: the log. |
70 log.SetString("scheme", FormSchemeToString(form.scheme)); | 263 if (logs.size() == 1) { |
71 log.SetString("signon realm", ScrubURL(GURL(form.signon_realm))); | 264 log_dict.Set(kIDsToMessages[logs[0].label], |
72 log.SetString("original signon realm", | 265 GetValueFromStructuredLog(logs[0]).release()); |
73 ScrubURL(GURL(form.original_signon_realm))); | 266 } else { |
74 log.SetString("origin", ScrubURL(form.origin)); | 267 if (logs.empty() || logs[0].log_type != StructuredLog::LOG_TYPE_LABEL) { |
75 log.SetString("action", ScrubURL(form.action)); | 268 NOTREACHED(); // The logs vector looks damaged. |
76 log.SetString("username element", form.username_element); | 269 return std::string(); |
77 log.SetString("password element", form.password_element); | 270 } |
78 log.SetBoolean("password autocomplete set", form.password_autocomplete_set); | 271 scoped_ptr<DictionaryValue> inner_dict(new DictionaryValue); |
79 log.SetString("old password element", form.old_password_element); | 272 // Transform and add all but the first log into |inner_dict|. |
80 log.SetBoolean("ssl valid", form.ssl_valid); | 273 for (size_t i = 1; i < logs.size(); ++i) { |
81 log.SetBoolean("password generated", | 274 inner_dict->Set(kIDsToMessages[logs[i].label], |
82 form.type == PasswordForm::TYPE_GENERATED); | 275 GetValueFromStructuredLog(logs[i]).release()); |
83 log.SetInteger("times used", form.times_used); | 276 } |
84 log.SetBoolean("use additional authentication", | 277 log_dict.Set(kIDsToMessages[logs[0].label], inner_dict.release()); |
85 form.use_additional_authentication); | 278 } |
86 log.SetBoolean("is PSL match", form.IsPublicSuffixMatch()); | 279 |
87 LogValue(message, log); | 280 // Now we convert the base::DictionaryValue to a string. |
88 } | |
89 | |
90 void SavePasswordProgressLogger::LogHTMLForm(const std::string& message, | |
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 } | |
100 | |
101 void SavePasswordProgressLogger::LogURL(const std::string& message, | |
102 const GURL& url) { | |
103 LogValue(message, StringValue(ScrubURL(url))); | |
104 } | |
105 | |
106 void SavePasswordProgressLogger::LogBoolean(const std::string& message, | |
107 bool value) { | |
108 LogValue(message, FundamentalValue(value)); | |
109 } | |
110 | |
111 void SavePasswordProgressLogger::LogNumber(const std::string& message, | |
112 int value) { | |
113 LogValue(message, FundamentalValue(value)); | |
114 } | |
115 | |
116 void SavePasswordProgressLogger::LogNumber(const std::string& message, | |
117 size_t value) { | |
118 LogValue(message, FundamentalValue(checked_cast<int, size_t>(value))); | |
119 } | |
120 | |
121 void SavePasswordProgressLogger::LogFinalDecision(Decision decision) { | |
122 LogValue("Final decision taken", DecisionToStringValue(decision)); | |
123 } | |
124 | |
125 void SavePasswordProgressLogger::LogMessage(const std::string& message) { | |
126 LogValue("Message", StringValue(message)); | |
127 } | |
128 | |
129 void SavePasswordProgressLogger::LogValue(const std::string& name, | |
130 const Value& log) { | |
131 std::string log_string; | 281 std::string log_string; |
132 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( | 282 bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( |
133 &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); | 283 &log_dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); |
134 DCHECK(conversion_to_string_successful); | 284 DCHECK(conversion_to_string_successful); |
135 SendLog(name + ": " + log_string); | 285 return log_string; |
| 286 } |
| 287 |
| 288 void SavePasswordProgressLogger::LogPasswordForm( |
| 289 SavePasswordProgressLogger::StringID label, |
| 290 const PasswordForm& form) { |
| 291 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 292 logs.reserve(15); |
| 293 logs.push_back(StructuredLog(label)); |
| 294 logs.push_back( |
| 295 StructuredLog(STRING_SCHEME_MESSAGE, FormSchemeToStringID(form.scheme))); |
| 296 logs.push_back(StructuredLog(STRING_SIGNON_REALM, GURL(form.signon_realm))); |
| 297 logs.push_back(StructuredLog(STRING_ORIGINAL_SIGNON_REALM, |
| 298 GURL(form.original_signon_realm))); |
| 299 logs.push_back(StructuredLog(STRING_ORIGIN, form.origin)); |
| 300 logs.push_back(StructuredLog(STRING_ACTION, form.action)); |
| 301 logs.push_back(StructuredLog(STRING_USERNAME_ELEMENT, form.username_element)); |
| 302 logs.push_back(StructuredLog(STRING_PASSWORD_ELEMENT, form.password_element)); |
| 303 logs.push_back(StructuredLog(STRING_PASSWORD_AUTOCOMPLETE_SET, |
| 304 form.password_autocomplete_set)); |
| 305 logs.push_back( |
| 306 StructuredLog(STRING_OLD_PASSWORD_ELEMENT, form.old_password_element)); |
| 307 logs.push_back(StructuredLog(STRING_SSL_VALID, form.ssl_valid)); |
| 308 logs.push_back(StructuredLog(STRING_PASSWORD_GENERATED, |
| 309 form.type == PasswordForm::TYPE_GENERATED)); |
| 310 logs.push_back(StructuredLog(STRING_TIMES_USED, form.times_used)); |
| 311 logs.push_back(StructuredLog(STRING_USE_ADDITIONAL_AUTHENTICATION, |
| 312 form.use_additional_authentication)); |
| 313 logs.push_back(StructuredLog(STRING_PSL_MATCH, form.IsPublicSuffixMatch())); |
| 314 SendLog(logs); |
| 315 } |
| 316 |
| 317 void SavePasswordProgressLogger::LogHTMLForm( |
| 318 SavePasswordProgressLogger::StringID label, |
| 319 const std::string& name_or_id, |
| 320 const std::string& method, |
| 321 const GURL& action) { |
| 322 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 323 logs.reserve(4); |
| 324 logs.push_back(StructuredLog(label)); |
| 325 logs.push_back(StructuredLog(STRING_NAME_OR_ID, name_or_id)); |
| 326 logs.push_back(StructuredLog(STRING_METHOD, FormMethodToStringID(method))); |
| 327 logs.push_back(StructuredLog(STRING_ACTION, action)); |
| 328 SendLog(logs); |
| 329 } |
| 330 |
| 331 void SavePasswordProgressLogger::LogURL( |
| 332 SavePasswordProgressLogger::StringID label, |
| 333 const GURL& url) { |
| 334 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 335 logs.push_back(StructuredLog(label, url)); |
| 336 SendLog(logs); |
| 337 } |
| 338 |
| 339 void SavePasswordProgressLogger::LogBoolean( |
| 340 SavePasswordProgressLogger::StringID label, |
| 341 bool value) { |
| 342 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 343 logs.push_back(StructuredLog(label, value)); |
| 344 SendLog(logs); |
| 345 } |
| 346 |
| 347 void SavePasswordProgressLogger::LogNumber( |
| 348 SavePasswordProgressLogger::StringID label, |
| 349 int value) { |
| 350 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 351 logs.push_back(StructuredLog(label, value)); |
| 352 SendLog(logs); |
| 353 } |
| 354 |
| 355 void SavePasswordProgressLogger::LogNumber( |
| 356 SavePasswordProgressLogger::StringID label, |
| 357 size_t value) { |
| 358 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 359 logs.push_back(StructuredLog(label, checked_cast<int, size_t>(value))); |
| 360 SendLog(logs); |
| 361 } |
| 362 |
| 363 void SavePasswordProgressLogger::LogMessage( |
| 364 SavePasswordProgressLogger::StringID message) { |
| 365 std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
| 366 logs.push_back(StructuredLog(STRING_MESSAGE, message)); |
| 367 SendLog(logs); |
136 } | 368 } |
137 | 369 |
138 } // namespace autofill | 370 } // namespace autofill |
OLD | NEW |