Index: components/autofill/core/common/save_password_progress_logger.cc |
diff --git a/components/autofill/core/common/save_password_progress_logger.cc b/components/autofill/core/common/save_password_progress_logger.cc |
index cdb8dd7a177e97f1542db30a714749683b3094f1..397b58762e44bb885cc06af7781a5f84560dc085 100644 |
--- a/components/autofill/core/common/save_password_progress_logger.cc |
+++ b/components/autofill/core/common/save_password_progress_logger.cc |
@@ -4,9 +4,15 @@ |
#include "components/autofill/core/common/save_password_progress_logger.h" |
+#include <algorithm> |
+ |
#include "base/json/json_writer.h" |
#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/memory/scoped_ptr.h" |
#include "base/numerics/safe_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
#include "base/values.h" |
#include "components/autofill/core/common/password_form.h" |
@@ -20,6 +26,42 @@ namespace autofill { |
namespace { |
+// These messages can be used as dictionary keys. Do not use '.' in them. |
+const char* const kIDsToMessages[] = { |
+ "Decision: ASK the user", // STRING_DECISION_ASK |
+ "Decision: DROP the password", // STRING_DECISION_DROP, |
+ "Decision: SAVE the password", // STRING_DECISION_SAVE, |
+ "Form method", // STRING_METHOD, |
+ "GET", // STRING_METHOD_GET, |
+ "POST", // STRING_METHOD_POST, |
+ "(empty)", // STRING_METHOD_EMPTY, |
+ "(other)", // STRING_OTHER, |
+ "HTML", // STRING_SCHEME_HTML, |
+ "Basic", // STRING_SCHEME_BASIC, |
+ "Digest", // STRING_SCHEME_DIGEST, |
+ "Scheme", // STRING_SCHEME_MESSAGE, |
+ "Signon realm", // STRING_SIGNON_REALM, |
+ "Original signon realm", // STRING_ORIGINAL_SIGNON_REALM, |
+ "Origin", // STRING_ORIGIN, |
+ "Action", // STRING_ACTION, |
+ "Username element", // STRING_USERNAME_ELEMENT, |
+ "Password element", // STRING_PASSWORD_ELEMENT, |
+ "Password autocomplete set", // STRING_PASSWORD_AUTOCOMPLETE_SET, |
+ "Old password element", // STRING_OLD_PASSWORD_ELEMENT, |
+ "SSL valid", // STRING_SSL_VALID, |
+ "Password generated", // STRING_PASSWORD_GENERATED, |
+ "Times used", // STRING_TIMES_USED, |
+ "Use additional authentication", // STRING_USE_ADDITIONAL_AUTHENTICATION, |
+ "PSL match", // STRING_PSL_MATCH, |
+ "Form name or ID", // STRING_NAME_OR_ID, |
+ "Message", // STRING_MESSAGE, |
+ "INVALID", // STRING_INVALID, |
+}; |
+ |
+COMPILE_ASSERT(SavePasswordProgressLogger::StringID::STRING_MAX + 1 == |
+ arraysize(kIDsToMessages), |
+ IDsToMessagesMappingIsOutOfDate); |
+ |
// Removes privacy sensitive parts of |url| (currently all but host and scheme). |
std::string ScrubURL(const GURL& url) { |
if (url.is_valid()) |
@@ -27,112 +69,302 @@ std::string ScrubURL(const GURL& url) { |
return std::string(); |
} |
-std::string FormSchemeToString(PasswordForm::Scheme scheme) { |
+// Returns true for all characters which we don't want to see in the logged IDs |
+// or names of HTML elements. |
+bool IsUnwantedInElementID (char c) { |
+ return !IsAsciiAlpha(c) && !IsAsciiDigit(c); |
+} |
+ |
+// Replaces all characters satisfying IsUnwantedInElementID by a ' ', and turns |
+// all characters to lowercase. This damages some valid HTML element IDs or |
+// names, but it is likely that it will be still possible to match the scrubbed |
+// string to the original ID or name in the HTML doc. That's good enough for the |
+// logging purposes, and provides some security benefits. |
+std::string ScrubElementID(std::string element_id) { |
+ std::replace_if( |
+ element_id.begin(), element_id.end(), IsUnwantedInElementID, ' '); |
+ return StringToLowerASCII(element_id); |
+} |
+ |
+SavePasswordProgressLogger::StringID FormSchemeToStringID( |
+ PasswordForm::Scheme scheme) { |
switch (scheme) { |
case PasswordForm::SCHEME_HTML: |
- return "HTML"; |
+ return SavePasswordProgressLogger::STRING_SCHEME_HTML; |
case PasswordForm::SCHEME_BASIC: |
- return "BASIC"; |
+ return SavePasswordProgressLogger::STRING_SCHEME_BASIC; |
case PasswordForm::SCHEME_DIGEST: |
- return "DIGEST"; |
+ return SavePasswordProgressLogger::STRING_SCHEME_DIGEST; |
case PasswordForm::SCHEME_OTHER: |
- return "OTHER"; |
+ return SavePasswordProgressLogger::STRING_OTHER; |
} |
NOTREACHED(); // Win compilers don't believe this is unreachable. |
- return std::string(); |
+ return SavePasswordProgressLogger::StringID::STRING_INVALID; |
+} |
+ |
+SavePasswordProgressLogger::StringID FormMethodToStringID( |
+ const std::string& method) { |
+ std::string method_processed; |
+ base::TrimWhitespaceASCII( |
+ StringToLowerASCII(method), base::TRIM_ALL, &method_processed); |
+ if (method_processed.empty()) |
+ return SavePasswordProgressLogger::STRING_METHOD_EMPTY; |
+ else if (method_processed == "get") |
+ return SavePasswordProgressLogger::STRING_METHOD_GET; |
+ else if (method_processed == "post") |
+ return SavePasswordProgressLogger::STRING_METHOD_POST; |
+ else |
+ return SavePasswordProgressLogger::STRING_OTHER; |
} |
-StringValue DecisionToStringValue( |
- SavePasswordProgressLogger::Decision decision) { |
- switch (decision) { |
- case SavePasswordProgressLogger::DECISION_SAVE: |
- return StringValue("SAVE the password"); |
- case SavePasswordProgressLogger::DECISION_ASK: |
- return StringValue("ASK the user whether to save the password"); |
- case SavePasswordProgressLogger::DECISION_DROP: |
- return StringValue("DROP the password"); |
+// Interprets |log|, sanitizes its value if needed, and returns it as |
+// base::Value. Does not work on LOG_TYPE_LABEL logs. |
+scoped_ptr<Value> GetValueFromStructuredLog( |
+ const SavePasswordProgressLogger::StructuredLog& log) { |
+ switch (log.log_type) { |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_LABEL: |
+ NOTREACHED(); |
+ return scoped_ptr<Value>(); |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_GURL: |
+ return scoped_ptr<Value>(Value::CreateStringValue(ScrubURL(log.url))); |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_MESSAGE: |
+ return scoped_ptr<Value>( |
+ Value::CreateStringValue(kIDsToMessages[log.message])); |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_NUMBER: |
+ return scoped_ptr<Value>(Value::CreateIntegerValue(log.number)); |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_BOOL: |
+ return scoped_ptr<Value>(Value::CreateBooleanValue(log.truth_value)); |
+ case SavePasswordProgressLogger::StructuredLog::LOG_TYPE_ELEMENT_ID: |
+ return scoped_ptr<Value>( |
+ Value::CreateStringValue(ScrubElementID(log.element_id))); |
} |
NOTREACHED(); // Win compilers don't believe this is unreachable. |
- return StringValue(std::string()); |
+ return scoped_ptr<Value>(); |
} |
} // namespace |
-SavePasswordProgressLogger::SavePasswordProgressLogger() {} |
+SavePasswordProgressLogger::StructuredLog::StructuredLog() |
+ : log_type(LOG_TYPE_LABEL), |
+ label(STRING_INVALID), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(false) { |
+} |
-SavePasswordProgressLogger::~SavePasswordProgressLogger() {} |
+SavePasswordProgressLogger::StructuredLog::StructuredLog( |
+ SavePasswordProgressLogger::StringID label) |
+ : log_type(LOG_TYPE_LABEL), |
+ label(label), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(false) { |
+} |
-void SavePasswordProgressLogger::LogPasswordForm(const std::string& message, |
- const PasswordForm& form) { |
- DictionaryValue log; |
- // Do not use the "<<" operator for PasswordForms, because it also prints |
- // passwords. Also, that operator is only for testing. |
- log.SetString("scheme", FormSchemeToString(form.scheme)); |
- log.SetString("signon realm", ScrubURL(GURL(form.signon_realm))); |
- log.SetString("original signon realm", |
- ScrubURL(GURL(form.original_signon_realm))); |
- log.SetString("origin", ScrubURL(form.origin)); |
- log.SetString("action", ScrubURL(form.action)); |
- log.SetString("username element", form.username_element); |
- log.SetString("password element", form.password_element); |
- log.SetBoolean("password autocomplete set", form.password_autocomplete_set); |
- log.SetString("old password element", form.old_password_element); |
- log.SetBoolean("ssl valid", form.ssl_valid); |
- log.SetBoolean("password generated", |
- form.type == PasswordForm::TYPE_GENERATED); |
- log.SetInteger("times used", form.times_used); |
- log.SetBoolean("use additional authentication", |
- form.use_additional_authentication); |
- log.SetBoolean("is PSL match", form.IsPublicSuffixMatch()); |
- LogValue(message, log); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
+ GURL url) |
+ : log_type(LOG_TYPE_GURL), |
+ label(label), |
+ url(url), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(false) { |
} |
-void SavePasswordProgressLogger::LogHTMLForm(const std::string& message, |
- const std::string& name_or_id, |
- const std::string& method, |
- const GURL& action) { |
- DictionaryValue log; |
- log.SetString("name_or_id", name_or_id); |
- log.SetString("method", method); |
- log.SetString("action", ScrubURL(action)); |
- LogValue(message, log); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
+ StringID message) |
+ : log_type(LOG_TYPE_MESSAGE), |
+ label(label), |
+ message(message), |
+ number(0), |
+ truth_value(false) { |
} |
-void SavePasswordProgressLogger::LogURL(const std::string& message, |
- const GURL& url) { |
- LogValue(message, StringValue(ScrubURL(url))); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
+ int number) |
+ : log_type(LOG_TYPE_NUMBER), |
+ label(label), |
+ message(STRING_INVALID), |
+ number(number), |
+ truth_value(false) { |
} |
-void SavePasswordProgressLogger::LogBoolean(const std::string& message, |
- bool value) { |
- LogValue(message, FundamentalValue(value)); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog(StringID label, |
+ bool truth_value) |
+ : log_type(LOG_TYPE_BOOL), |
+ label(label), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(truth_value) { |
} |
-void SavePasswordProgressLogger::LogNumber(const std::string& message, |
- int value) { |
- LogValue(message, FundamentalValue(value)); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog( |
+ StringID label, |
+ const base::string16& element_id) |
+ : log_type(LOG_TYPE_ELEMENT_ID), |
+ label(label), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(false), |
+ element_id(UTF16ToUTF8(element_id)) { |
} |
-void SavePasswordProgressLogger::LogNumber(const std::string& message, |
- size_t value) { |
- LogValue(message, FundamentalValue(checked_cast<int, size_t>(value))); |
+SavePasswordProgressLogger::StructuredLog::StructuredLog( |
+ StringID label, |
+ const std::string& element_id) |
+ : log_type(LOG_TYPE_ELEMENT_ID), |
+ label(label), |
+ message(STRING_INVALID), |
+ number(0), |
+ truth_value(false), |
+ element_id(element_id) { |
} |
-void SavePasswordProgressLogger::LogFinalDecision(Decision decision) { |
- LogValue("Final decision taken", DecisionToStringValue(decision)); |
+SavePasswordProgressLogger::StructuredLog::~StructuredLog() { |
} |
-void SavePasswordProgressLogger::LogMessage(const std::string& message) { |
- LogValue("Message", StringValue(message)); |
+bool SavePasswordProgressLogger::StructuredLog::operator==( |
+ const SavePasswordProgressLogger::StructuredLog& log) const { |
+ if (log_type != log.log_type || label != log.label) |
+ return false; |
+ switch (log_type) { |
+ case LOG_TYPE_LABEL: |
+ return true; |
+ case LOG_TYPE_GURL: |
+ return url == log.url; |
+ case LOG_TYPE_MESSAGE: |
+ return message == log.message; |
+ case LOG_TYPE_NUMBER: |
+ return number == log.number; |
+ case LOG_TYPE_BOOL: |
+ return truth_value == log.truth_value; |
+ case LOG_TYPE_ELEMENT_ID: |
+ return element_id == log.element_id; |
+ }; |
+ NOTREACHED(); // Win compilers don't believe this is unreachable. |
+ return false; |
} |
-void SavePasswordProgressLogger::LogValue(const std::string& name, |
- const Value& log) { |
+bool SavePasswordProgressLogger::StructuredLog::operator!=( |
+ const SavePasswordProgressLogger::StructuredLog& log) const { |
+ return !operator==(log); |
+} |
+ |
+ |
+SavePasswordProgressLogger::SavePasswordProgressLogger() {} |
+ |
+SavePasswordProgressLogger::~SavePasswordProgressLogger() {} |
+ |
+// static |
+std::string SavePasswordProgressLogger::SanitizeStructuredLogs( |
+ const std::vector<SavePasswordProgressLogger::StructuredLog>& logs) { |
+ // First, we sanitize the logs and transform them into a base::Value. |
+ DictionaryValue log_dict; // Always holds a single item: the log. |
+ if (logs.size() == 1) { |
+ log_dict.Set(kIDsToMessages[logs[0].label], |
+ GetValueFromStructuredLog(logs[0]).release()); |
+ } else { |
+ if (logs.empty() || logs[0].log_type != StructuredLog::LOG_TYPE_LABEL) { |
+ NOTREACHED(); // The logs vector looks damaged. |
+ return std::string(); |
+ } |
+ scoped_ptr<DictionaryValue> inner_dict(new DictionaryValue); |
+ // Transform and add all but the first log into |inner_dict|. |
+ for (size_t i = 1; i < logs.size(); ++i) { |
+ inner_dict->Set(kIDsToMessages[logs[i].label], |
+ GetValueFromStructuredLog(logs[i]).release()); |
+ } |
+ log_dict.Set(kIDsToMessages[logs[0].label], inner_dict.release()); |
+ } |
+ |
+ // Now we convert the base::DictionaryValue to a string. |
std::string log_string; |
bool conversion_to_string_successful = base::JSONWriter::WriteWithOptions( |
- &log, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); |
+ &log_dict, base::JSONWriter::OPTIONS_PRETTY_PRINT, &log_string); |
DCHECK(conversion_to_string_successful); |
- SendLog(name + ": " + log_string); |
+ return log_string; |
+} |
+ |
+void SavePasswordProgressLogger::LogPasswordForm( |
+ SavePasswordProgressLogger::StringID label, |
+ const PasswordForm& form) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.reserve(15); |
+ logs.push_back(StructuredLog(label)); |
+ logs.push_back( |
+ StructuredLog(STRING_SCHEME_MESSAGE, FormSchemeToStringID(form.scheme))); |
+ logs.push_back(StructuredLog(STRING_SIGNON_REALM, GURL(form.signon_realm))); |
+ logs.push_back(StructuredLog(STRING_ORIGINAL_SIGNON_REALM, |
+ GURL(form.original_signon_realm))); |
+ logs.push_back(StructuredLog(STRING_ORIGIN, form.origin)); |
+ logs.push_back(StructuredLog(STRING_ACTION, form.action)); |
+ logs.push_back(StructuredLog(STRING_USERNAME_ELEMENT, form.username_element)); |
+ logs.push_back(StructuredLog(STRING_PASSWORD_ELEMENT, form.password_element)); |
+ logs.push_back(StructuredLog(STRING_PASSWORD_AUTOCOMPLETE_SET, |
+ form.password_autocomplete_set)); |
+ logs.push_back( |
+ StructuredLog(STRING_OLD_PASSWORD_ELEMENT, form.old_password_element)); |
+ logs.push_back(StructuredLog(STRING_SSL_VALID, form.ssl_valid)); |
+ logs.push_back(StructuredLog(STRING_PASSWORD_GENERATED, |
+ form.type == PasswordForm::TYPE_GENERATED)); |
+ logs.push_back(StructuredLog(STRING_TIMES_USED, form.times_used)); |
+ logs.push_back(StructuredLog(STRING_USE_ADDITIONAL_AUTHENTICATION, |
+ form.use_additional_authentication)); |
+ logs.push_back(StructuredLog(STRING_PSL_MATCH, form.IsPublicSuffixMatch())); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogHTMLForm( |
+ SavePasswordProgressLogger::StringID label, |
+ const std::string& name_or_id, |
+ const std::string& method, |
+ const GURL& action) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.reserve(4); |
+ logs.push_back(StructuredLog(label)); |
+ logs.push_back(StructuredLog(STRING_NAME_OR_ID, name_or_id)); |
+ logs.push_back(StructuredLog(STRING_METHOD, FormMethodToStringID(method))); |
+ logs.push_back(StructuredLog(STRING_ACTION, action)); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogURL( |
+ SavePasswordProgressLogger::StringID label, |
+ const GURL& url) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.push_back(StructuredLog(label, url)); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogBoolean( |
+ SavePasswordProgressLogger::StringID label, |
+ bool value) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.push_back(StructuredLog(label, value)); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogNumber( |
+ SavePasswordProgressLogger::StringID label, |
+ int value) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.push_back(StructuredLog(label, value)); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogNumber( |
+ SavePasswordProgressLogger::StringID label, |
+ size_t value) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.push_back(StructuredLog(label, checked_cast<int, size_t>(value))); |
+ SendLog(logs); |
+} |
+ |
+void SavePasswordProgressLogger::LogMessage( |
+ SavePasswordProgressLogger::StringID message) { |
+ std::vector<SavePasswordProgressLogger::StructuredLog> logs; |
+ logs.push_back(StructuredLog(STRING_MESSAGE, message)); |
+ SendLog(logs); |
} |
} // namespace autofill |