summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormvrable@chromium.org <mvrable@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-22 21:16:28 +0000
committermvrable@chromium.org <mvrable@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-22 21:16:28 +0000
commitf61d0ccdd443ebce5eecaa7bbe75f98a660f6772 (patch)
treeca63c84b502f6959ab29d49d0fc26b5d04c9522d
parentc1682a16d6454677595d97837276e98c0dcc4264 (diff)
downloadchromium_src-f61d0ccdd443ebce5eecaa7bbe75f98a660f6772.zip
chromium_src-f61d0ccdd443ebce5eecaa7bbe75f98a660f6772.tar.gz
chromium_src-f61d0ccdd443ebce5eecaa7bbe75f98a660f6772.tar.bz2
Improved extension activity logging for the chrome.webRequest API.
Add a new type of log item in the activitylog_urls table: WEBREQUEST, used to summarize the changes made to an HTTP request using the blocking WebRequest extension API. The types of modifications made are always logged when the extension activity log is enabled; the details of the modification are only kept if the activity log testing flag is enabled (as the details may contain sensitive data). BUG=169628 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=195265 Review URL: https://chromiumcodereview.appspot.com/12491012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195596 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/activity_log.cc44
-rw-r--r--chrome/browser/extensions/activity_log.h8
-rw-r--r--chrome/browser/extensions/activity_log_web_request_constants.cc33
-rw-r--r--chrome/browser/extensions/activity_log_web_request_constants.h36
-rw-r--r--chrome/browser/extensions/api/web_request/web_request_api.cc168
-rw-r--r--chrome/browser/extensions/dom_actions.cc4
-rw-r--r--chrome/browser/extensions/dom_actions.h9
-rw-r--r--chrome/chrome_browser_extensions.gypi2
-rw-r--r--chrome/test/data/extensions/activity_log/options.js22
9 files changed, 319 insertions, 7 deletions
diff --git a/chrome/browser/extensions/activity_log.cc b/chrome/browser/extensions/activity_log.cc
index 92c46d3..6fe5f52 100644
--- a/chrome/browser/extensions/activity_log.cc
+++ b/chrome/browser/extensions/activity_log.cc
@@ -356,6 +356,50 @@ void ActivityLog::LogDOMAction(const Extension* extension,
DOMAction::MODIFIED);
}
+void ActivityLog::LogWebRequestAction(const Extension* extension,
+ const GURL& url,
+ const std::string& api_call,
+ scoped_ptr<DictionaryValue> details,
+ const std::string& extra) {
+ string16 null_title;
+ if (!IsLogEnabled()) return;
+
+ // Strip details of the web request modifications (for privacy reasons),
+ // unless testing is enabled.
+ if (!testing_mode_) {
+ DictionaryValue::Iterator details_iterator(*details);
+ while (!details_iterator.IsAtEnd()) {
+ details->SetBoolean(details_iterator.key(), true);
+ details_iterator.Advance();
+ }
+ }
+ std::string details_string;
+ JSONStringValueSerializer serializer(&details_string);
+ serializer.SerializeAndOmitBinaryValues(*details);
+
+ scoped_refptr<DOMAction> action = new DOMAction(
+ extension->id(),
+ base::Time::Now(),
+ DOMAction::WEBREQUEST,
+ url,
+ null_title,
+ api_call,
+ details_string,
+ extra);
+ ScheduleAndForget(&ActivityDatabase::RecordAction, action);
+
+ // Display the action.
+ ObserverMap::const_iterator iter = observers_.find(extension);
+ if (iter != observers_.end()) {
+ iter->second->Notify(&Observer::OnExtensionActivity,
+ extension,
+ ActivityLog::ACTIVITY_CONTENT_SCRIPT,
+ action->PrettyPrintForDebug());
+ }
+ if (log_activity_to_stdout_)
+ LOG(INFO) << action->PrettyPrintForDebug();
+}
+
void ActivityLog::GetActions(
const std::string& extension_id,
const int day,
diff --git a/chrome/browser/extensions/activity_log.h b/chrome/browser/extensions/activity_log.h
index 55c7b18..2095d7a 100644
--- a/chrome/browser/extensions/activity_log.h
+++ b/chrome/browser/extensions/activity_log.h
@@ -109,6 +109,14 @@ class ActivityLog : public ProfileKeyedService,
const ListValue* args, // arguments
const std::string& extra); // extra logging info
+ // Log a use of the WebRequest API to redirect, cancel, or modify page
+ // headers.
+ void LogWebRequestAction(const Extension* extension,
+ const GURL& url,
+ const std::string& api_call,
+ scoped_ptr<base::DictionaryValue> details,
+ const std::string& extra);
+
// Retrieves the list of actions for a given extension on a specific day.
// Today is 0, yesterday is 1, etc. Returns one day at a time.
// Response is sent to the method/function in the callback.
diff --git a/chrome/browser/extensions/activity_log_web_request_constants.cc b/chrome/browser/extensions/activity_log_web_request_constants.cc
new file mode 100644
index 0000000..97c65a2
--- /dev/null
+++ b/chrome/browser/extensions/activity_log_web_request_constants.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 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.
+
+// Constants used when describing request modifications via the WebRequest API
+// in the activity log.
+
+#include "chrome/browser/extensions/activity_log_web_request_constants.h"
+
+namespace activity_log_web_request_constants {
+
+// Keys used in the dictionary summarizing an EventResponseDelta for the
+// extension activity log.
+const char kCancelKey[] = "cancel";
+const char kNewUrlKey[] = "new_url";
+const char kModifiedRequestHeadersKey[] = "modified_request_headers";
+const char kDeletedRequestHeadersKey[] = "deleted_request_headers";
+const char kAddedRequestHeadersKey[] = "added_request_headers";
+const char kDeletedResponseHeadersKey[] = "deleted_response_headers";
+const char kAuthCredentialsKey[] = "auth_credentials";
+const char kResponseCookieModificationsKey[] = "response_cookie_modifications";
+
+// Keys and values used for describing cookie modifications.
+const char kCookieModificationTypeKey[] = "type";
+const char kCookieModificationAdd[] = "ADD";
+const char kCookieModificationEdit[] = "EDIT";
+const char kCookieModificationRemove[] = "REMOVE";
+const char kCookieFilterNameKey[] = "filter_name";
+const char kCookieFilterDomainKey[] = "filter_domain";
+const char kCookieModNameKey[] = "mod_name";
+const char kCookieModDomainKey[] = "mod_domain";
+
+} // namespace activity_log_web_request_constants
diff --git a/chrome/browser/extensions/activity_log_web_request_constants.h b/chrome/browser/extensions/activity_log_web_request_constants.h
new file mode 100644
index 0000000..f94385c
--- /dev/null
+++ b/chrome/browser/extensions/activity_log_web_request_constants.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2013 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.
+
+// Constants used when describing request modifications via the WebRequest API
+// in the activity log.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_WEB_REQUEST_CONSTANTS_H_
+#define CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_WEB_REQUEST_CONSTANTS_H_
+
+namespace activity_log_web_request_constants {
+
+// Keys used in the dictionary summarizing an EventResponseDelta for the
+// extension activity log.
+extern const char kCancelKey[];
+extern const char kNewUrlKey[];
+extern const char kModifiedRequestHeadersKey[];
+extern const char kDeletedRequestHeadersKey[];
+extern const char kAddedRequestHeadersKey[];
+extern const char kDeletedResponseHeadersKey[];
+extern const char kAuthCredentialsKey[];
+extern const char kResponseCookieModificationsKey[];
+
+// Keys and values used for describing cookie modifications.
+extern const char kCookieModificationTypeKey[];
+extern const char kCookieModificationAdd[];
+extern const char kCookieModificationEdit[];
+extern const char kCookieModificationRemove[];
+extern const char kCookieFilterNameKey[];
+extern const char kCookieFilterDomainKey[];
+extern const char kCookieModNameKey[];
+extern const char kCookieModDomainKey[];
+
+} // namespace activity_log_web_request_constants
+
+#endif // CHROME_BROWSER_EXTENSIONS_ACTIVITY_LOG_WEB_REQUEST_CONSTANTS_H_
diff --git a/chrome/browser/extensions/api/web_request/web_request_api.cc b/chrome/browser/extensions/api/web_request/web_request_api.cc
index 623af7f..27ef5a3 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api.cc
@@ -17,6 +17,8 @@
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
+#include "chrome/browser/extensions/activity_log.h"
+#include "chrome/browser/extensions/activity_log_web_request_constants.h"
#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry.h"
@@ -77,6 +79,7 @@ namespace helpers = extension_web_request_api_helpers;
namespace keys = extension_web_request_api_constants;
namespace web_request = extensions::api::web_request;
namespace declarative_keys = extensions::declarative_webrequest_constants;
+namespace activitylog = activity_log_web_request_constants;
namespace {
@@ -1401,6 +1404,157 @@ helpers::EventResponseDelta* CalculateDelta(
return NULL;
}
+Value* SerializeResponseHeaders(const helpers::ResponseHeaders& headers) {
+ scoped_ptr<ListValue> serialized_headers(new ListValue());
+ for (helpers::ResponseHeaders::const_iterator i = headers.begin();
+ i != headers.end(); ++i) {
+ serialized_headers->Append(ToHeaderDictionary(i->first, i->second));
+ }
+ return serialized_headers.release();
+}
+
+// Convert a RequestCookieModifications/ResponseCookieModifications object to a
+// ListValue which summarizes the changes made. This is templated since the
+// two types (request/response) are different but contain essentially the same
+// fields.
+template<typename CookieType>
+ListValue* SummarizeCookieModifications(
+ const std::vector<linked_ptr<CookieType> >& modifications) {
+ scoped_ptr<ListValue> cookie_modifications(new ListValue());
+ for (typename std::vector<linked_ptr<CookieType> >::const_iterator i =
+ modifications.begin();
+ i != modifications.end(); ++i) {
+ scoped_ptr<DictionaryValue> summary(new DictionaryValue());
+ const CookieType& mod = *i->get();
+ switch (mod.type) {
+ case helpers::ADD:
+ summary->SetString(activitylog::kCookieModificationTypeKey,
+ activitylog::kCookieModificationAdd);
+ break;
+ case helpers::EDIT:
+ summary->SetString(activitylog::kCookieModificationTypeKey,
+ activitylog::kCookieModificationEdit);
+ break;
+ case helpers::REMOVE:
+ summary->SetString(activitylog::kCookieModificationTypeKey,
+ activitylog::kCookieModificationRemove);
+ break;
+ }
+ if (mod.filter) {
+ if (mod.filter->name)
+ summary->SetString(activitylog::kCookieFilterNameKey,
+ *mod.modification->name);
+ if (mod.filter->domain)
+ summary->SetString(activitylog::kCookieFilterDomainKey,
+ *mod.modification->name);
+ }
+ if (mod.modification) {
+ if (mod.modification->name)
+ summary->SetString(activitylog::kCookieModDomainKey,
+ *mod.modification->name);
+ if (mod.modification->domain)
+ summary->SetString(activitylog::kCookieModDomainKey,
+ *mod.modification->name);
+ }
+ cookie_modifications->Append(summary.release());
+ }
+ return cookie_modifications.release();
+}
+
+// Converts an EventResponseDelta object to a dictionary value suitable for the
+// activity log. The caller takes ownership of the returned DictionaryValue
+// object.
+DictionaryValue* SummarizeResponseDelta(
+ const std::string& event_name,
+ const helpers::EventResponseDelta& delta) {
+ scoped_ptr<DictionaryValue> details(new DictionaryValue());
+ if (delta.cancel) {
+ details->SetBoolean(activitylog::kCancelKey, true);
+ }
+ if (!delta.new_url.is_empty()) {
+ details->SetString(activitylog::kNewUrlKey, delta.new_url.spec());
+ }
+
+ scoped_ptr<ListValue> modified_headers(new ListValue());
+ net::HttpRequestHeaders::Iterator iter(delta.modified_request_headers);
+ while (iter.GetNext()) {
+ modified_headers->Append(ToHeaderDictionary(iter.name(), iter.value()));
+ }
+ if (!modified_headers->empty()) {
+ details->Set(activitylog::kModifiedRequestHeadersKey,
+ modified_headers.release());
+ }
+
+ scoped_ptr<ListValue> deleted_headers(new ListValue());
+ deleted_headers->AppendStrings(delta.deleted_request_headers);
+ if (!deleted_headers->empty()) {
+ details->Set(activitylog::kDeletedRequestHeadersKey,
+ deleted_headers.release());
+ }
+
+ if (!delta.added_response_headers.empty()) {
+ details->Set(activitylog::kAddedRequestHeadersKey,
+ SerializeResponseHeaders(delta.added_response_headers));
+ }
+ if (!delta.deleted_response_headers.empty()) {
+ details->Set(activitylog::kDeletedResponseHeadersKey,
+ SerializeResponseHeaders(delta.deleted_response_headers));
+ }
+ if (delta.auth_credentials) {
+ details->SetString(activitylog::kAuthCredentialsKey,
+ UTF16ToUTF8(delta.auth_credentials->username()) + ":*");
+ }
+
+ if (!delta.response_cookie_modifications.empty()) {
+ details->Set(
+ activitylog::kResponseCookieModificationsKey,
+ SummarizeCookieModifications(delta.response_cookie_modifications));
+ }
+
+ return details.release();
+}
+
+void LogExtensionActivity(Profile* profile,
+ const std::string& extension_id,
+ const GURL& url,
+ const std::string& api_call,
+ DictionaryValue* details_raw) {
+ scoped_ptr<DictionaryValue> details(details_raw);
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&LogExtensionActivity,
+ profile,
+ extension_id,
+ url,
+ api_call,
+ details.release()));
+ } else {
+ // An ExtensionService might not be running during unit tests, or an
+ // extension might have been unloadd by the time we get to logging it. In
+ // those cases log a warning.
+ ExtensionService* extension_service =
+ extensions::ExtensionSystem::Get(profile)->extension_service();
+ if (!extension_service) {
+ LOG(WARNING) << "ExtensionService does not seem to be available "
+ << "(this may be normal for unit tests)";
+ } else {
+ const Extension* extension =
+ extension_service->extensions()->GetByID(extension_id);
+ if (!extension) {
+ LOG(WARNING) << "Extension " << extension_id << " not found!";
+ } else {
+ extensions::ActivityLog::GetInstance(profile)->LogWebRequestAction(
+ extension,
+ url,
+ api_call,
+ details.Pass(),
+ "");
+ }
+ }
+ }
+}
+
} // namespace
void ExtensionWebRequestEventRouter::DecrementBlockCount(
@@ -1421,9 +1575,19 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount(
CHECK_GE(num_handlers_blocking, 0);
if (response) {
+ helpers::EventResponseDelta* delta =
+ CalculateDelta(&blocked_request, response);
+
+ if (extensions::ActivityLog::IsLogEnabled()) {
+ LogExtensionActivity(static_cast<Profile*>(profile),
+ extension_id,
+ blocked_request.request->url(),
+ event_name,
+ SummarizeResponseDelta(event_name, *delta));
+ }
+
blocked_request.response_deltas.push_back(
- linked_ptr<helpers::EventResponseDelta>(
- CalculateDelta(&blocked_request, response)));
+ linked_ptr<helpers::EventResponseDelta>(delta));
}
base::TimeDelta block_time =
diff --git a/chrome/browser/extensions/dom_actions.cc b/chrome/browser/extensions/dom_actions.cc
index 2d3b6f4..ffb8665 100644
--- a/chrome/browser/extensions/dom_actions.cc
+++ b/chrome/browser/extensions/dom_actions.cc
@@ -108,6 +108,8 @@ std::string DOMAction::VerbAsString() const {
return "INSERTED";
case XHR:
return "XHR";
+ case WEBREQUEST:
+ return "WEBREQUEST";
default:
NOTREACHED();
return NULL;
@@ -124,6 +126,8 @@ DOMAction::DOMActionType DOMAction::StringAsDOMActionType(
return INSERTED;
} else if (str == "XHR") {
return XHR;
+ } else if (str == "WEBREQUEST") {
+ return WEBREQUEST;
} else {
NOTREACHED();
return MODIFIED; // this should never happen!
diff --git a/chrome/browser/extensions/dom_actions.h b/chrome/browser/extensions/dom_actions.h
index a4a238b..13d5e07 100644
--- a/chrome/browser/extensions/dom_actions.h
+++ b/chrome/browser/extensions/dom_actions.h
@@ -16,10 +16,11 @@ namespace extensions {
class DOMAction : public Action {
public:
enum DOMActionType {
- MODIFIED, // For Content Script DOM manipulations
- READ, // For Content Script DOM manipulations
- INSERTED, // For when Content Scripts are added to pages
- XHR, // When an extension core sends an XHR
+ MODIFIED, // For Content Script DOM manipulations
+ READ, // For Content Script DOM manipulations
+ INSERTED, // For when Content Scripts are added to pages
+ XHR, // When an extension core sends an XHR
+ WEBREQUEST, // When a page request is modified with the WebRequest API
};
static const char* kTableName;
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 70553eb..896c285 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -67,6 +67,8 @@
'browser/extensions/activity_database.h',
'browser/extensions/activity_log.cc',
'browser/extensions/activity_log.h',
+ 'browser/extensions/activity_log_web_request_constants.cc',
+ 'browser/extensions/activity_log_web_request_constants.h',
'browser/extensions/admin_policy.cc',
'browser/extensions/admin_policy.h',
'browser/extensions/api_actions.cc',
diff --git a/chrome/test/data/extensions/activity_log/options.js b/chrome/test/data/extensions/activity_log/options.js
index 1298b30..8498e71 100644
--- a/chrome/test/data/extensions/activity_log/options.js
+++ b/chrome/test/data/extensions/activity_log/options.js
@@ -156,18 +156,38 @@ function doWebRequestModifications() {
// Install a webRequest handler that will add an HTTP header to the outgoing
// request for the main page.
function doModifyHeaders(details) {
+ var response = {};
+
var headers = details.requestHeaders;
if (headers === undefined) {
headers = [];
}
headers.push({'name': 'X-Test-Activity-Log-Send',
'value': 'Present'});
- return {'requestHeaders': headers};
+ response['requestHeaders'] = headers;
+
+ headers = details.responseHeaders;
+ if (headers === undefined) {
+ headers = [];
+ }
+ headers = headers.filter(
+ function(x) {return x["name"] != "Cache-Control"});
+ headers.push({'name': 'X-Test-Response-Header',
+ 'value': 'Inserted'});
+ headers.push({'name': 'Set-Cookie',
+ 'value': 'ActivityLog=InsertedCookie'});
+ response['responseHeaders'] = headers;
+
+ return response;
}
chrome.webRequest.onBeforeSendHeaders.addListener(
doModifyHeaders,
{'urls': ['http://*/*'], 'types': ['main_frame']},
['blocking', 'requestHeaders']);
+ chrome.webRequest.onHeadersReceived.addListener(
+ doModifyHeaders,
+ {'urls': ['http://*/*'], 'types': ['main_frame']},
+ ['blocking', 'responseHeaders']);
// Open a tab, then close it when it has finished loading--this should give
// the webRequest handler a chance to run.