diff options
author | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-18 15:44:05 +0000 |
---|---|---|
committer | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-18 15:44:05 +0000 |
commit | b4a916070db5632e52244479ffd5bc717296b8d9 (patch) | |
tree | c3c8b16efac6fcc39950f343f4ad5923971a4f3d | |
parent | 7aa5fe5abe8ee6c59f4a0ad8e5cac1701c4c9af2 (diff) | |
download | chromium_src-b4a916070db5632e52244479ffd5bc717296b8d9.zip chromium_src-b4a916070db5632e52244479ffd5bc717296b8d9.tar.gz chromium_src-b4a916070db5632e52244479ffd5bc717296b8d9.tar.bz2 |
Allow removing private data in chrome://net-export.
This introduces a new LogLevel, LOG_STRIP_PRIVATE_DATA, which callbacks within
the network stack check to decide whether or not to report redacted data.
Unfortunately, this involves duplicating the net-internals implementation, but
that implementation isn't easily reusable given chrome://net-exports'
constraints.
Plumb this state through net-export and adjust the UI and state machine
accordingly. Add various tests.
This also moves HttpAuth::ChallengeTokenizer to HttpUtil::ChallengeTokenizer as
some of the redaction logic reuses the parser. This avoids giving everything a
dependency on HttpAuth.
BUG=349502
Review URL: https://codereview.chromium.org/182523006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257645 0039d316-1c4b-4281-b951-d872f2087c98
22 files changed, 418 insertions, 148 deletions
diff --git a/chrome/browser/net/net_log_temp_file.cc b/chrome/browser/net/net_log_temp_file.cc index bfc7cd1..626d296 100644 --- a/chrome/browser/net/net_log_temp_file.cc +++ b/chrome/browser/net/net_log_temp_file.cc @@ -15,6 +15,7 @@ using content::BrowserThread; NetLogTempFile::NetLogTempFile(ChromeNetLog* chrome_net_log) : state_(STATE_UNINITIALIZED), + log_type_(LOG_TYPE_NONE), log_filename_(FILE_PATH_LITERAL("chrome-net-export-log.json")), chrome_net_log_(chrome_net_log) { } @@ -31,7 +32,10 @@ void NetLogTempFile::ProcessCommand(Command command) { switch (command) { case DO_START: - StartNetLog(); + StartNetLog(false); + break; + case DO_START_STRIP_PRIVATE_DATA: + StartNetLog(true); break; case DO_STOP: StopNetLog(); @@ -53,19 +57,32 @@ base::DictionaryValue* NetLogTempFile::GetState() { #endif // NDEBUG switch (state_) { - case STATE_ALLOW_START: - dict->SetString("state", "ALLOW_START"); - break; - case STATE_ALLOW_STOP: - dict->SetString("state", "ALLOW_STOP"); + case STATE_NOT_LOGGING: + dict->SetString("state", "NOT_LOGGING"); break; - case STATE_ALLOW_START_SEND: - dict->SetString("state", "ALLOW_START_SEND"); + case STATE_LOGGING: + dict->SetString("state", "LOGGING"); break; - default: + case STATE_UNINITIALIZED: dict->SetString("state", "UNINITIALIZED"); break; } + + switch (log_type_) { + case LOG_TYPE_NONE: + dict->SetString("logType", "NONE"); + break; + case LOG_TYPE_UNKNOWN: + dict->SetString("logType", "UNKNOWN"); + break; + case LOG_TYPE_NORMAL: + dict->SetString("logType", "NORMAL"); + break; + case LOG_TYPE_STRIP_PRIVATE_DATA: + dict->SetString("logType", "STRIP_PRIVATE_DATA"); + break; + } + return dict; } @@ -77,17 +94,18 @@ bool NetLogTempFile::EnsureInit() { if (!GetNetExportLog()) return false; + state_ = STATE_NOT_LOGGING; if (NetExportLogExists()) - state_ = STATE_ALLOW_START_SEND; + log_type_ = LOG_TYPE_UNKNOWN; else - state_ = STATE_ALLOW_START; + log_type_ = LOG_TYPE_NONE; return true; } -void NetLogTempFile::StartNetLog() { +void NetLogTempFile::StartNetLog(bool strip_private_data) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING)); - if (state_ == STATE_ALLOW_STOP) + if (state_ == STATE_LOGGING) return; DCHECK_NE(STATE_UNINITIALIZED, state_); @@ -102,23 +120,29 @@ void NetLogTempFile::StartNetLog() { scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants()); net_log_logger_.reset(new net::NetLogLogger(file, *constants)); + if (strip_private_data) { + net_log_logger_->set_log_level(net::NetLog::LOG_STRIP_PRIVATE_DATA); + log_type_ = LOG_TYPE_STRIP_PRIVATE_DATA; + } else { + log_type_ = LOG_TYPE_NORMAL; + } net_log_logger_->StartObserving(chrome_net_log_); - state_ = STATE_ALLOW_STOP; + state_ = STATE_LOGGING; } void NetLogTempFile::StopNetLog() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING)); - if (state_ != STATE_ALLOW_STOP) + if (state_ != STATE_LOGGING) return; net_log_logger_->StopObserving(); net_log_logger_.reset(); - state_ = STATE_ALLOW_START_SEND; + state_ = STATE_NOT_LOGGING; } bool NetLogTempFile::GetFilePath(base::FilePath* path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING)); - if (state_ != STATE_ALLOW_START_SEND) + if (log_type_ == LOG_TYPE_NONE || state_ == STATE_LOGGING) return false; if (!NetExportLogExists()) diff --git a/chrome/browser/net/net_log_temp_file.h b/chrome/browser/net/net_log_temp_file.h index 0890cfe..0138446 100644 --- a/chrome/browser/net/net_log_temp_file.h +++ b/chrome/browser/net/net_log_temp_file.h @@ -25,13 +25,14 @@ class ChromeNetLog; // NetLogTempFile logs all the NetLog entries into a temporary file // "chrome-net-export-log.json" created in base::GetTempDir() directory. // -// NetLogTempFile maintains the current state (state_) of the logging into a -// chrome-net-export-log.json file. +// NetLogTempFile maintains the current logging state (state_) and log file type +// (log_type_) of the logging into a chrome-net-export-log.json file. // // The following are the possible states -// a) Only Start is allowed (state_ == STATE_UNINITIALIZED). -// b) Only Stop is allowed (state_ == STATE_ALLOW_STOP). -// c) Either Send or Start is allowed (state_ == STATE_ALLOW_START_SEND). +// a) Only Start is allowed (STATE_NOT_LOGGING, LOG_TYPE_NONE). +// b) Only Stop is allowed (STATE_LOGGING). +// c) Either Send or Start is allowed (STATE_NOT_LOGGING, anything but +// LOG_TYPE_NONE). // // This is created/destroyed on the UI thread, but all other function calls // occur on the FILE_USER_BLOCKING thread. @@ -42,8 +43,9 @@ class NetLogTempFile { public: // This enum lists the UI button commands it could receive. enum Command { - DO_START, // Call StartLog. - DO_STOP, // Call StopLog. + DO_START, // Call StartNetLog. + DO_START_STRIP_PRIVATE_DATA, // Call StartNetLog stripping private data. + DO_STOP, // Call StopNetLog. }; virtual ~NetLogTempFile(); // Destructs a NetLogTempFile. @@ -88,21 +90,36 @@ class NetLogTempFile { // to enable/disable "Start", "Stop" and "Send" (email) UI actions. enum State { STATE_UNINITIALIZED, - STATE_ALLOW_START, // Only DO_START Command is allowed. - STATE_ALLOW_STOP, // Only DO_STOP Command is allowed. - STATE_ALLOW_START_SEND, // Either DO_START or DO_SEND is allowed. + // Not currently logging to file. + STATE_NOT_LOGGING, + // Currently logging to file. + STATE_LOGGING, }; - // Initializes the |state_| to either STATE_ALLOW_START (if there is no - // temporary file from earlier run) or STATE_ALLOW_START_SEND (if there is a - // temporary file from earlier run). Returns false if initialization of - // |log_path_| fails. + // The type of the current log file on disk. + enum LogType { + // There is no current log file. + LOG_TYPE_NONE, + // The file predates this session. May or may not have private data. + // TODO(davidben): This state is kind of silly. + LOG_TYPE_UNKNOWN, + // The file has credentials and cookies stripped. + LOG_TYPE_STRIP_PRIVATE_DATA, + // The file includes all data. + LOG_TYPE_NORMAL, + }; + + // Initializes the |state_| to STATE_NOT_LOGGING and |log_type_| to + // LOG_TYPE_NONE (if there is no temporary file from earlier run) or + // LOG_TYPE_UNKNOWN (if there is a temporary file from earlier run). Returns + // false if initialization of |log_path_| fails. bool EnsureInit(); // Start collecting NetLog data into chrome-net-export-log.json file in - // base::GetTempDir() directory. It is a no-op if we are already - // collecting data into a file. - void StartNetLog(); + // base::GetTempDir() directory. If |strip_private_data| is true, do not log + // cookies and credentials. It is a no-op if we are already collecting data + // into a file. + void StartNetLog(bool strip_private_data); // Stop collecting NetLog data into the temporary file. It is a no-op if we // are not collecting data into a file. @@ -115,8 +132,10 @@ class NetLogTempFile { // Helper function for unit tests. State state() const { return state_; } + LogType log_type() const { return log_type_; } State state_; // Current state of NetLogTempFile. + LogType log_type_; // Type of current log file on disk. // Name of the file. It defaults to chrome-net-export-log.json, but can be // overwritten by unit tests. diff --git a/chrome/browser/net/net_log_temp_file_unittest.cc b/chrome/browser/net/net_log_temp_file_unittest.cc index ba5b6f3..0782226 100644 --- a/chrome/browser/net/net_log_temp_file_unittest.cc +++ b/chrome/browser/net/net_log_temp_file_unittest.cc @@ -88,6 +88,13 @@ class NetLogTempFileTest : public ::testing::Test { return state; } + std::string GetLogTypeString() const { + scoped_ptr<base::DictionaryValue> dict(net_log_temp_file_->GetState()); + std::string log_type; + EXPECT_TRUE(dict->GetString("logType", &log_type)); + return log_type; + } + // Make sure the export file has been created and is non-empty, as net // constants will always be written to it on creation. void VerifyNetExportLog() { @@ -113,8 +120,10 @@ class NetLogTempFileTest : public ::testing::Test { // When we lie in NetExportLogExists, make sure state and GetFilePath return // correct values. void VerifyFilePathAndStateAfterEnsureInit() { - EXPECT_EQ("ALLOW_START", GetStateString()); - EXPECT_EQ(NetLogTempFile::STATE_ALLOW_START, net_log_temp_file_->state()); + EXPECT_EQ("NOT_LOGGING", GetStateString()); + EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); + EXPECT_EQ("NONE", GetLogTypeString()); + EXPECT_EQ(NetLogTempFile::LOG_TYPE_NONE, net_log_temp_file_->log_type()); base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); @@ -123,21 +132,41 @@ class NetLogTempFileTest : public ::testing::Test { // Make sure the export file has been successfully initialized. void VerifyFileAndStateAfterDoStart() { - EXPECT_EQ("ALLOW_STOP", GetStateString()); - EXPECT_EQ(NetLogTempFile::STATE_ALLOW_STOP, net_log_temp_file_->state()); + EXPECT_EQ("LOGGING", GetStateString()); + EXPECT_EQ(NetLogTempFile::STATE_LOGGING, net_log_temp_file_->state()); + EXPECT_EQ("NORMAL", GetLogTypeString()); + EXPECT_EQ(NetLogTempFile::LOG_TYPE_NORMAL, net_log_temp_file_->log_type()); // Check GetFilePath returns false, if we are still writing to file. base::FilePath net_export_file_path; EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); VerifyNetExportLog(); + EXPECT_EQ(net::NetLog::LOG_ALL_BUT_BYTES, net_log_->GetLogLevel()); + } + + // Make sure the export file has been successfully initialized. + void VerifyFileAndStateAfterDoStartStripPrivateData() { + EXPECT_EQ("LOGGING", GetStateString()); + EXPECT_EQ(NetLogTempFile::STATE_LOGGING, net_log_temp_file_->state()); + EXPECT_EQ("STRIP_PRIVATE_DATA", GetLogTypeString()); + EXPECT_EQ(NetLogTempFile::LOG_TYPE_STRIP_PRIVATE_DATA, + net_log_temp_file_->log_type()); + + // Check GetFilePath returns false, if we are still writing to file. + base::FilePath net_export_file_path; + EXPECT_FALSE(net_log_temp_file_->GetFilePath(&net_export_file_path)); + + VerifyNetExportLog(); + EXPECT_EQ(net::NetLog::LOG_STRIP_PRIVATE_DATA, net_log_->GetLogLevel()); } // Make sure the export file has been successfully initialized. void VerifyFileAndStateAfterDoStop() { - EXPECT_EQ("ALLOW_START_SEND", GetStateString()); - EXPECT_EQ(NetLogTempFile::STATE_ALLOW_START_SEND, - net_log_temp_file_->state()); + EXPECT_EQ("NOT_LOGGING", GetStateString()); + EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); + EXPECT_EQ("NORMAL", GetLogTypeString()); + EXPECT_EQ(NetLogTempFile::LOG_TYPE_NORMAL, net_log_temp_file_->log_type()); base::FilePath net_export_file_path; EXPECT_TRUE(net_log_temp_file_->GetFilePath(&net_export_file_path)); @@ -182,9 +211,10 @@ TEST_F(NetLogTempFileTest, EnsureInitAllowStart) { TEST_F(NetLogTempFileTest, EnsureInitAllowStartOrSend) { EXPECT_TRUE(net_log_temp_file_->EnsureInit()); - EXPECT_EQ("ALLOW_START_SEND", GetStateString()); - EXPECT_EQ(NetLogTempFile::STATE_ALLOW_START_SEND, - net_log_temp_file_->state()); + EXPECT_EQ("NOT_LOGGING", GetStateString()); + EXPECT_EQ(NetLogTempFile::STATE_NOT_LOGGING, net_log_temp_file_->state()); + EXPECT_EQ("UNKNOWN", GetLogTypeString()); + EXPECT_EQ(NetLogTempFile::LOG_TYPE_UNKNOWN, net_log_temp_file_->log_type()); EXPECT_EQ(net_export_log_, net_log_temp_file_->log_path_); EXPECT_TRUE(base::PathExists(net_export_log_)); diff --git a/chrome/browser/resources/net_export/net_export.html b/chrome/browser/resources/net_export/net_export.html index 105e5143..86fede4 100644 --- a/chrome/browser/resources/net_export/net_export.html +++ b/chrome/browser/resources/net_export/net_export.html @@ -14,14 +14,31 @@ <h2>NetLog Export</h2> <div id="net-export-main"> <div> - <button id="export-view-start-data">Start Logging to Disk</button> - <span class="warning">Deletes old log</span> + <label> + <input id="export-view-private-data-toggle" + type="checkbox" checked disabled> + Strip private information (cookies and credentials) + </label> </div> <div> - <button id="export-view-stop-data">Stop Logging</button> + <button id="export-view-start-data" disabled> + Start Logging to Disk + </button> + <span class="warning" id="export-view-deletes-log-text" hidden> + Deletes old log + </span> </div> <div> - <button id="export-view-send-data">Email Log</button> + <button id="export-view-stop-data" disabled>Stop Logging</button> + </div> + <div> + <button id="export-view-send-data" disabled>Email Log</button> + <span class="warning" id="export-view-private-data-text" hidden> + Log contains private information + </span> + <span class="warning" id="export-view-send-old-log-text" hidden> + Log file from previous session + </span> </div> <pre id="export-view-file-path-text"></pre> <p> @@ -35,10 +52,11 @@ of desktop Chrome. </p> <p> - <span class="warning">WARNING</span>: Logs contain a list of sites - visited, cookies, and credentials from when logging started to when - logging stopped. They may also contain general network - configuration information, such as DNS and proxy configuration. + <span class="warning">WARNING</span>: Logs contain a list of sites visited + from when logging started to when logging stopped. They may also contain + general network configuration information, such as DNS and proxy + configuration. If private information is not stripped, the logs also + contain cookies and credentials. </p> </div> </body> diff --git a/chrome/browser/resources/net_export/net_export.js b/chrome/browser/resources/net_export/net_export.js index 799a643..c9521b4 100644 --- a/chrome/browser/resources/net_export/net_export.js +++ b/chrome/browser/resources/net_export/net_export.js @@ -24,23 +24,14 @@ var NetExportView = (function() { /** @const */ var POLL_INTERVAL_MS = 5000; // -------------------------------------------------------------------------- - // Important IDs in the HTML document - // -------------------------------------------------------------------------- - - /** @const */ var START_DATA_BUTTON_ID = 'export-view-start-data'; - /** @const */ var STOP_DATA_BUTTON_ID = 'export-view-stop-data'; - /** @const */ var SEND_DATA_BUTTON_ID = 'export-view-send-data'; - /** @const */ var FILE_PATH_TEXT_ID = 'export-view-file-path-text'; - - // -------------------------------------------------------------------------- /** * @constructor */ function NetExportView() { - $(START_DATA_BUTTON_ID).onclick = this.onStartData_.bind(this); - $(STOP_DATA_BUTTON_ID).onclick = this.onStopData_.bind(this); - $(SEND_DATA_BUTTON_ID).onclick = this.onSendData_.bind(this); + $('export-view-start-data').onclick = this.onStartData_.bind(this); + $('export-view-stop-data').onclick = this.onStopData_.bind(this); + $('export-view-send-data').onclick = this.onSendData_.bind(this); window.setInterval(function() { chrome.send('getExportNetLogInfo'); }, POLL_INTERVAL_MS); @@ -55,7 +46,8 @@ var NetExportView = (function() { * Starts saving NetLog data to a file. */ onStartData_: function() { - chrome.send('startNetLog'); + var stripPrivateData = $('export-view-private-data-toggle').checked; + chrome.send('startNetLog', [stripPrivateData]); }, /** @@ -73,34 +65,51 @@ var NetExportView = (function() { }, /** - * Enable or disable START_DATA_BUTTON_ID, STOP_DATA_BUTTON_ID and - * SEND_DATA_BUTTON_ID buttons. Displays the path name of the file where - * NetLog data is collected. + * Updates the UI to reflect the current state. Displays the path name of + * the file where NetLog data is collected. */ onExportNetLogInfoChanged: function(exportNetLogInfo) { if (exportNetLogInfo.file) { var message = ''; - if (exportNetLogInfo.state == 'ALLOW_STOP') + if (exportNetLogInfo.state == 'LOGGING') message = 'NetLog data is collected in: '; - else if (exportNetLogInfo.state == 'ALLOW_START_SEND') + else if (exportNetLogInfo.logType != 'NONE') message = 'NetLog data to send is in: '; - $(FILE_PATH_TEXT_ID).textContent = message + exportNetLogInfo.file; + $('export-view-file-path-text').textContent = + message + exportNetLogInfo.file; } else { - $(FILE_PATH_TEXT_ID).textContent = ''; + $('export-view-file-path-text').textContent = ''; } - $(START_DATA_BUTTON_ID).disabled = true; - $(STOP_DATA_BUTTON_ID).disabled = true; - $(SEND_DATA_BUTTON_ID).disabled = true; - if (exportNetLogInfo.state == 'ALLOW_START') { - $(START_DATA_BUTTON_ID).disabled = false; - } else if (exportNetLogInfo.state == 'ALLOW_STOP') { - $(STOP_DATA_BUTTON_ID).disabled = false; - } else if (exportNetLogInfo.state == 'ALLOW_START_SEND') { - $(START_DATA_BUTTON_ID).disabled = false; - $(SEND_DATA_BUTTON_ID).disabled = false; + $('export-view-private-data-toggle').disabled = true; + $('export-view-start-data').disabled = true; + $('export-view-deletes-log-text').hidden = true; + $('export-view-stop-data').disabled = true; + $('export-view-send-data').disabled = true; + $('export-view-private-data-text').hidden = true; + $('export-view-send-old-log-text').hidden = true; + if (exportNetLogInfo.state == 'NOT_LOGGING') { + // Allow making a new log. + $('export-view-private-data-toggle').disabled = false; + $('export-view-start-data').disabled = false; + + // If there's an existing log, allow sending it. + if (exportNetLogInfo.logType != 'NONE') { + $('export-view-deletes-log-text').hidden = false; + $('export-view-send-data').disabled = false; + if (exportNetLogInfo.logType == 'UNKNOWN') { + $('export-view-send-old-log-text').hidden = false; + } else if (exportNetLogInfo.logType == 'NORMAL') { + $('export-view-private-data-text').hidden = false; + } + } + } else if (exportNetLogInfo.state == 'LOGGING') { + // Only possible to stop logging. Checkbox reflects current state. + $('export-view-private-data-toggle').checked = + (exportNetLogInfo.logType == 'STRIP_PRIVATE_DATA'); + $('export-view-stop-data').disabled = false; } else if (exportNetLogInfo.state == 'UNINITIALIZED') { - $(FILE_PATH_TEXT_ID).textContent = + $('export-view-file-path-text').textContent = 'Unable to initialize NetLog data file.'; } } diff --git a/chrome/browser/resources/net_internals/log_view_painter.js b/chrome/browser/resources/net_internals/log_view_painter.js index 656700e..667b1c6 100644 --- a/chrome/browser/resources/net_internals/log_view_painter.js +++ b/chrome/browser/resources/net_internals/log_view_painter.js @@ -522,6 +522,9 @@ function stripCookieOrLoginInfo(line) { * unencrypted login text removed. Otherwise, returns original |entry| object. * This is needed so that JSON log dumps can be made without affecting the * source data. Converts headers stored in objects to arrays. + * + * Note: this logic should be kept in sync with + * net::ElideHeaderForNetLog in net/http/http_log_util.cc. */ stripCookiesAndLoginInfo = function(entry) { if (!entry.params || entry.params.headers === undefined || diff --git a/chrome/browser/ui/webui/net_export_ui.cc b/chrome/browser/ui/webui/net_export_ui.cc index d4182f2..70caf26 100644 --- a/chrome/browser/ui/webui/net_export_ui.cc +++ b/chrome/browser/ui/webui/net_export_ui.cc @@ -141,9 +141,16 @@ void NetExportMessageHandler::OnGetExportNetLogInfo( } void NetExportMessageHandler::OnStartNetLog(const base::ListValue* list) { + bool strip_private_data = false; + if (!list->GetBoolean(0, &strip_private_data)) { + NOTREACHED() << "Failed to convert argument 1"; + return; + } ProcessNetLogCommand(weak_ptr_factory_.GetWeakPtr(), net_log_temp_file_, - NetLogTempFile::DO_START); + (strip_private_data ? + NetLogTempFile::DO_START_STRIP_PRIVATE_DATA : + NetLogTempFile::DO_START)); } void NetExportMessageHandler::OnStopNetLog(const base::ListValue* list) { diff --git a/net/base/net_log.h b/net/base/net_log.h index e8e7598..b037a33 100644 --- a/net/base/net_log.h +++ b/net/base/net_log.h @@ -78,6 +78,10 @@ class NET_EXPORT NetLog { // parameters for bytes sent/received events. LOG_ALL_BUT_BYTES, + // Log all events, but do not include the actual transferred bytes and + // remove cookies and HTTP credentials. + LOG_STRIP_PRIVATE_DATA, + // Don't log any events. LOG_NONE, }; diff --git a/net/base/net_log_logger.cc b/net/base/net_log_logger.cc index 39db82a..9653e59 100644 --- a/net/base/net_log_logger.cc +++ b/net/base/net_log_logger.cc @@ -23,7 +23,7 @@ namespace net { static const int kLogFormatVersion = 1; NetLogLogger::NetLogLogger(FILE* file, const base::Value& constants) - : file_(file), added_events_(false) { + : file_(file), log_level_(NetLog::LOG_ALL_BUT_BYTES), added_events_(false) { DCHECK(file); // Write constants to the output file. This allows loading files that have @@ -40,8 +40,13 @@ NetLogLogger::~NetLogLogger() { fprintf(file_.get(), "]}"); } +void NetLogLogger::set_log_level(net::NetLog::LogLevel log_level) { + DCHECK(!net_log()); + log_level_ = log_level; +} + void NetLogLogger::StartObserving(net::NetLog* net_log) { - net_log->AddThreadSafeObserver(this, net::NetLog::LOG_ALL_BUT_BYTES); + net_log->AddThreadSafeObserver(this, log_level_); } void NetLogLogger::StopObserving() { @@ -163,6 +168,8 @@ base::DictionaryValue* NetLogLogger::GetConstants() { dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL); dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES); + dict->SetInteger("LOG_STRIP_PRIVATE_DATA", + net::NetLog::LOG_STRIP_PRIVATE_DATA); constants_dict->Set("logLevelType", dict); } diff --git a/net/base/net_log_logger.h b/net/base/net_log_logger.h index 1d0bc5b..c827760 100644 --- a/net/base/net_log_logger.h +++ b/net/base/net_log_logger.h @@ -29,6 +29,9 @@ class NET_EXPORT NetLogLogger : public NetLog::ThreadSafeObserver { NetLogLogger(FILE* file, const base::Value& constants); virtual ~NetLogLogger(); + // Sets the log level to log at. Must be called before StartObserving. + void set_log_level(NetLog::LogLevel log_level); + // Starts observing specified NetLog. Must not already be watching a NetLog. // Separate from constructor to enforce thread safety. void StartObserving(NetLog* net_log); @@ -46,6 +49,9 @@ class NET_EXPORT NetLogLogger : public NetLog::ThreadSafeObserver { private: ScopedStdioHandle file_; + // The LogLevel to log at. + NetLog::LogLevel log_level_; + // True if OnAddEntry() has been called at least once. bool added_events_; diff --git a/net/http/http_auth_challenge_tokenizer.h b/net/http/http_auth_challenge_tokenizer.h index 3c93d25..a73f1920 100644 --- a/net/http/http_auth_challenge_tokenizer.h +++ b/net/http/http_auth_challenge_tokenizer.h @@ -37,6 +37,8 @@ class NET_EXPORT_PRIVATE HttpAuthChallengeTokenizer { return std::string(scheme_begin_, scheme_end_); } + std::string::const_iterator params_begin() const { return params_begin_; } + std::string::const_iterator params_end() const { return params_end_; } HttpUtil::NameValuePairsIterator param_pairs() const; std::string base64_param() const; diff --git a/net/http/http_log_util.cc b/net/http/http_log_util.cc new file mode 100644 index 0000000..ab6ebda --- /dev/null +++ b/net/http/http_log_util.cc @@ -0,0 +1,81 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_log_util.h" + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "net/http/http_auth_challenge_tokenizer.h" + +namespace net { + +namespace { + +bool ShouldRedactChallenge(HttpAuthChallengeTokenizer* challenge) { + // Ignore lines with commas, as they may contain lists of schemes, and + // the information we want to hide is Base64 encoded, so has no commas. + if (challenge->challenge_text().find(',') != std::string::npos) + return false; + + std::string scheme = StringToLowerASCII(challenge->scheme()); + // Invalid input. + if (scheme.empty()) + return false; + + // Ignore Basic and Digest authentication challenges, as they contain + // public information. + if (scheme == "basic" || scheme == "digest") + return false; + + return true; +} + +} // namespace + +std::string ElideHeaderValueForNetLog(NetLog::LogLevel log_level, + const std::string& header, + const std::string& value) { +#if defined(SPDY_PROXY_AUTH_ORIGIN) + if (!base::strcasecmp(header.c_str(), "proxy-authorization") || + !base::strcasecmp(header.c_str(), "proxy-authenticate")) { + return "[elided]"; + } +#endif + + if (log_level < NetLog::LOG_STRIP_PRIVATE_DATA) + return value; + + // Note: this logic should be kept in sync with stripCookiesAndLoginInfo in + // chrome/browser/resources/net_internals/log_view_painter.js. + + std::string::const_iterator redact_begin = value.begin(); + std::string::const_iterator redact_end = value.begin(); + if (!base::strcasecmp(header.c_str(), "set-cookie") || + !base::strcasecmp(header.c_str(), "set-cookie2") || + !base::strcasecmp(header.c_str(), "cookie") || + !base::strcasecmp(header.c_str(), "authorization") || + !base::strcasecmp(header.c_str(), "proxy-authorization")) { + redact_begin = value.begin(); + redact_end = value.end(); + } else if (!base::strcasecmp(header.c_str(), "www-authenticate") || + !base::strcasecmp(header.c_str(), "proxy-authenticate")) { + // Look for authentication information from data received from the server in + // multi-round Negotiate authentication. + HttpAuthChallengeTokenizer challenge(value.begin(), value.end()); + if (ShouldRedactChallenge(&challenge)) { + redact_begin = challenge.params_begin(); + redact_end = challenge.params_end(); + } + } + + if (redact_begin == redact_end) + return value; + + return std::string(value.begin(), redact_begin) + + base::StringPrintf("[%ld bytes were stripped]", + static_cast<long>(redact_end - redact_begin)) + + std::string(redact_end, value.end()); +} + +} // namespace net diff --git a/net/http/http_log_util.h b/net/http/http_log_util.h new file mode 100644 index 0000000..f18c6e7 --- /dev/null +++ b/net/http/http_log_util.h @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_HTTP_HTTP_LOG_UTIL_ +#define NET_HTTP_HTTP_LOG_UTIL_ + +#include <string> + +#include "net/base/net_export.h" +#include "net/base/net_log.h" + +namespace net { + +// Given an HTTP header |header| with value |value|, returns the elided version +// of the header value at |log_level|. +NET_EXPORT_PRIVATE std::string ElideHeaderValueForNetLog( + NetLog::LogLevel log_level, + const std::string& header, + const std::string& value); + +} // namespace net + +#endif // NET_HTTP_HTTP_LOG_UTIL_ diff --git a/net/http/http_log_util_unittest.cc b/net/http/http_log_util_unittest.cc new file mode 100644 index 0000000..1b8cdf2 --- /dev/null +++ b/net/http/http_log_util_unittest.cc @@ -0,0 +1,69 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/http/http_log_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(HttpLogUtilTest, ElideHeaderValueForNetLog) { + // Only elide for appropriate log level. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Cookie", "name=value")); + EXPECT_EQ("name=value", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, "Cookie", "name=value")); + + // Headers are compared case insensitively. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "cOoKiE", "name=value")); + + // These headers should be completely elided. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie2", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Authorization", "Basic 1234")); + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authorization", "Basic 1234")); + + // Unknown headers should pass through. + EXPECT_EQ("value", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Boring", "value")); + + // Basic and Digest auth challenges are public. + EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "WWW-Authenticate", "Basic realm=test")); + EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "WWW-Authenticate", "Digest realm=test")); + EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authenticate", "Basic realm=test")); + EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authenticate", "Digest realm=test")); + + // Multi-round mechanisms partially elided. + EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234")); + EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Proxy-Authenticate", "NTLM 1234")); + + // Leave whitespace intact. + EXPECT_EQ("NTLM [4 bytes were stripped] ", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234 ")); + +#if defined(SPDY_PROXY_AUTH_ORIGIN) + EXPECT_EQ("[elided]", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, "Proxy-Authorization", "Basic 1234")); +#else + EXPECT_EQ("Basic 1234", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, "Proxy-Authorization", "Basic 1234")); +#endif +} + +} // namspace net diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc index 8c9c428..9348e3e 100644 --- a/net/http/http_request_headers.cc +++ b/net/http/http_request_headers.cc @@ -9,20 +9,9 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" +#include "net/http/http_log_util.h" #include "net/http/http_util.h" -namespace { - -bool ShouldShowHttpHeaderValue(const std::string& header_name) { -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (header_name == "Proxy-Authorization") - return false; -#endif - return true; -} - -} // namespace - namespace net { const char HttpRequestHeaders::kGetMethod[] = "GET"; @@ -197,17 +186,17 @@ std::string HttpRequestHeaders::ToString() const { base::Value* HttpRequestHeaders::NetLogCallback( const std::string* request_line, - NetLog::LogLevel /* log_level */) const { + NetLog::LogLevel log_level) const { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("line", *request_line); base::ListValue* headers = new base::ListValue(); for (HeaderVector::const_iterator it = headers_.begin(); it != headers_.end(); ++it) { + std::string log_value = ElideHeaderValueForNetLog( + log_level, it->key, it->value); headers->Append(new base::StringValue( base::StringPrintf("%s: %s", - it->key.c_str(), - (ShouldShowHttpHeaderValue(it->key) ? - it->value.c_str() : "[elided]")))); + it->key.c_str(), log_value.c_str()))); } dict->Set("headers", headers); return dict; diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index dbe09a6..f6e3658 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -23,6 +23,7 @@ #include "base/values.h" #include "net/base/escape.h" #include "net/http/http_byte_range.h" +#include "net/http/http_log_util.h" #include "net/http/http_util.h" using base::StringPiece; @@ -115,14 +116,6 @@ void CheckDoesNotHaveEmbededNulls(const std::string& str) { CHECK(str.find('\0') == std::string::npos); } -bool ShouldShowHttpHeaderValue(const std::string& header_name) { -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (header_name == "Proxy-Authenticate") - return false; -#endif - return true; -} - } // namespace const char HttpResponseHeaders::kContentRange[] = "Content-Range"; @@ -1338,7 +1331,7 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, } base::Value* HttpResponseHeaders::NetLogCallback( - NetLog::LogLevel /* log_level */) const { + NetLog::LogLevel log_level) const { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* headers = new base::ListValue(); headers->Append(new base::StringValue(GetStatusLine())); @@ -1346,12 +1339,10 @@ base::Value* HttpResponseHeaders::NetLogCallback( std::string name; std::string value; while (EnumerateHeaderLines(&iterator, &name, &value)) { + std::string log_value = ElideHeaderValueForNetLog(log_level, name, value); headers->Append( new base::StringValue( - base::StringPrintf("%s: %s", - name.c_str(), - (ShouldShowHttpHeaderValue(name) ? - value.c_str() : "[elided]")))); + base::StringPrintf("%s: %s", name.c_str(), log_value.c_str()))); } dict->Set("headers", headers); return dict; diff --git a/net/net.gyp b/net/net.gyp index 7e20f5f..bd408e2 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -595,6 +595,8 @@ 'http/http_content_disposition.h', 'http/http_chunked_decoder.cc', 'http/http_chunked_decoder.h', + 'http/http_log_util.cc', + 'http/http_log_util.h', 'http/http_network_layer.cc', 'http/http_network_layer.h', 'http/http_network_session.cc', @@ -1809,6 +1811,7 @@ 'http/http_cache_unittest.cc', 'http/http_chunked_decoder_unittest.cc', 'http/http_content_disposition_unittest.cc', + 'http/http_log_util_unittest.cc', 'http/http_network_layer_unittest.cc', 'http/http_network_transaction_ssl_unittest.cc', 'http/http_network_transaction_unittest.cc', diff --git a/net/spdy/spdy_header_block.cc b/net/spdy/spdy_header_block.cc index ecc22a4..bbe9c716 100644 --- a/net/spdy/spdy_header_block.cc +++ b/net/spdy/spdy_header_block.cc @@ -5,13 +5,13 @@ #include "net/spdy/spdy_header_block.h" #include "base/values.h" -#include "net/spdy/spdy_http_utils.h" +#include "net/http/http_log_util.h" namespace net { base::Value* SpdyHeaderBlockNetLogCallback( const SpdyHeaderBlock* headers, - NetLog::LogLevel /* log_level */) { + NetLog::LogLevel log_level) { base::DictionaryValue* dict = new base::DictionaryValue(); base::DictionaryValue* headers_dict = new base::DictionaryValue(); for (SpdyHeaderBlock::const_iterator it = headers->begin(); @@ -19,7 +19,7 @@ base::Value* SpdyHeaderBlockNetLogCallback( headers_dict->SetWithoutPathExpansion( it->first, new base::StringValue( - ShouldShowHttpHeaderValue(it->first) ? it->second : "[elided]")); + ElideHeaderValueForNetLog(log_level, it->first, it->second))); } dict->Set("headers", headers_dict); return dict; diff --git a/net/spdy/spdy_http_utils.cc b/net/spdy/spdy_http_utils.cc index da95b51..76a0031 100644 --- a/net/spdy/spdy_http_utils.cc +++ b/net/spdy/spdy_http_utils.cc @@ -193,12 +193,4 @@ GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, return GURL(url); } -bool ShouldShowHttpHeaderValue(const std::string& header_name) { -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (header_name == "proxy-authorization") - return false; -#endif - return true; -} - } // namespace net diff --git a/net/spdy/spdy_http_utils.h b/net/spdy/spdy_http_utils.h index 6a91c888..34a0fcd 100644 --- a/net/spdy/spdy_http_utils.h +++ b/net/spdy/spdy_http_utils.h @@ -42,10 +42,6 @@ GURL GetUrlFromHeaderBlock(const SpdyHeaderBlock& headers, SpdyMajorVersion protocol_version, bool pushed); -// Returns true if the value of this header should be displayed. -NET_EXPORT_PRIVATE bool ShouldShowHttpHeaderValue( - const std::string& header_name); - NET_EXPORT_PRIVATE SpdyPriority ConvertRequestPriorityToSpdyPriority( RequestPriority priority, SpdyMajorVersion protocol_version); diff --git a/net/spdy/spdy_http_utils_unittest.cc b/net/spdy/spdy_http_utils_unittest.cc index daac323..d811164 100644 --- a/net/spdy/spdy_http_utils_unittest.cc +++ b/net/spdy/spdy_http_utils_unittest.cc @@ -52,16 +52,6 @@ TEST(SpdyHttpUtilsTest, ConvertSpdy3PriorityToRequestPriority) { } } -TEST(SpdyHttpUtilsTest, ShowHttpHeaderValue){ -#if defined(SPDY_PROXY_AUTH_ORIGIN) - EXPECT_FALSE(ShouldShowHttpHeaderValue("proxy-authorization")); - EXPECT_TRUE(ShouldShowHttpHeaderValue("accept-encoding")); -#else - EXPECT_TRUE(ShouldShowHttpHeaderValue("proxy-authorization")); - EXPECT_TRUE(ShouldShowHttpHeaderValue("accept-encoding")); -#endif -} - } // namespace } // namespace net diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index ac30a52..c20d89e 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -29,8 +29,10 @@ #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/cert/asn1_util.h" +#include "net/http/http_log_util.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties.h" +#include "net/http/http_util.h" #include "net/spdy/spdy_buffer_producer.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_http_utils.h" @@ -54,13 +56,14 @@ const SpdyStreamId kFirstStreamId = 1; const int kMinPushedStreamLifetimeSeconds = 300; scoped_ptr<base::ListValue> SpdyHeaderBlockToListValue( - const SpdyHeaderBlock& headers) { + const SpdyHeaderBlock& headers, + net::NetLog::LogLevel log_level) { scoped_ptr<base::ListValue> headers_list(new base::ListValue()); for (SpdyHeaderBlock::const_iterator it = headers.begin(); it != headers.end(); ++it) { headers_list->AppendString( it->first + ": " + - (ShouldShowHttpHeaderValue(it->first) ? it->second : "[elided]")); + ElideHeaderValueForNetLog(log_level, it->first, it->second)); } return headers_list.Pass(); } @@ -70,9 +73,10 @@ base::Value* NetLogSpdySynStreamSentCallback(const SpdyHeaderBlock* headers, bool unidirectional, SpdyPriority spdy_priority, SpdyStreamId stream_id, - NetLog::LogLevel /* log_level */) { + NetLog::LogLevel log_level) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release()); + dict->Set("headers", + SpdyHeaderBlockToListValue(*headers, log_level).release()); dict->SetBoolean("fin", fin); dict->SetBoolean("unidirectional", unidirectional); dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority)); @@ -87,9 +91,10 @@ base::Value* NetLogSpdySynStreamReceivedCallback( SpdyPriority spdy_priority, SpdyStreamId stream_id, SpdyStreamId associated_stream, - NetLog::LogLevel /* log_level */) { + NetLog::LogLevel log_level) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release()); + dict->Set("headers", + SpdyHeaderBlockToListValue(*headers, log_level).release()); dict->SetBoolean("fin", fin); dict->SetBoolean("unidirectional", unidirectional); dict->SetInteger("spdy_priority", static_cast<int>(spdy_priority)); @@ -102,9 +107,10 @@ base::Value* NetLogSpdySynReplyOrHeadersReceivedCallback( const SpdyHeaderBlock* headers, bool fin, SpdyStreamId stream_id, - NetLog::LogLevel /* log_level */) { + NetLog::LogLevel log_level) { base::DictionaryValue* dict = new base::DictionaryValue(); - dict->Set("headers", SpdyHeaderBlockToListValue(*headers).release()); + dict->Set("headers", + SpdyHeaderBlockToListValue(*headers, log_level).release()); dict->SetBoolean("fin", fin); dict->SetInteger("stream_id", stream_id); return dict; |