summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-24 17:49:08 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-24 17:49:08 +0000
commit05c82189519642144323493e1d0cd65c41ce81ce (patch)
treeabb760e7c2d610ab059eec1222fc3d15b6b30db5 /chrome/browser/extensions
parenteb40bc39aafb7933251450019e5b4bcb805982f5 (diff)
downloadchromium_src-05c82189519642144323493e1d0cd65c41ce81ce.zip
chromium_src-05c82189519642144323493e1d0cd65c41ce81ce.tar.gz
chromium_src-05c82189519642144323493e1d0cd65c41ce81ce.tar.bz2
Require user opt-in before allowing content script injection on file URLs.
BUG=47180 Review URL: http://codereview.chromium.org/2809034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50737 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.cc4
-rw-r--r--chrome/browser/extensions/extension_prefs.cc15
-rw-r--r--chrome/browser/extensions/extension_prefs.h5
-rw-r--r--chrome/browser/extensions/extension_startup_browsertest.cc18
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc3
-rw-r--r--chrome/browser/extensions/extensions_service.cc37
-rw-r--r--chrome/browser/extensions/extensions_service.h11
-rw-r--r--chrome/browser/extensions/extensions_ui.cc38
-rw-r--r--chrome/browser/extensions/extensions_ui.h3
-rw-r--r--chrome/browser/extensions/user_script_master.cc22
10 files changed, 153 insertions, 3 deletions
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc
index c391db8..44de6bf 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/execute_code_in_tab_function.cc
@@ -9,6 +9,7 @@
#include "chrome/browser/browser.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
+#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/extensions/file_reader.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
@@ -66,7 +67,8 @@ bool ExecuteCodeInTabFunction::RunImpl() {
// NOTE: This can give the wrong answer due to race conditions, but it is OK,
// we check again in the renderer.
- if (!GetExtension()->CanExecuteScriptOnHost(contents->GetURL(), &error_))
+ if (!profile()->GetExtensionsService()->CanExecuteScriptOnHost(
+ GetExtension(), contents->GetURL(), &error_))
return false;
if (script_info->HasKey(keys::kAllFramesKey)) {
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index e73b2f6..0f9661c 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -67,6 +67,10 @@ const wchar_t kIdleInstallInfoFetchTime[] = L"fetch_time";
// A preference that, if true, will allow this extension to run in incognito
// mode.
const wchar_t kPrefIncognitoEnabled[] = L"incognito";
+
+// A preference to control whether an extension is allowed to inject script in
+// pages with file URLs.
+const wchar_t kPrefAllowFileAccess[] = L"allowFileAccess";
}
////////////////////////////////////////////////////////////////////////////////
@@ -352,6 +356,17 @@ void ExtensionPrefs::SetIsIncognitoEnabled(const std::string& extension_id,
prefs_->SavePersistentPrefs();
}
+bool ExtensionPrefs::AllowFileAccess(const std::string& extension_id) {
+ return ReadExtensionPrefBoolean(extension_id, kPrefAllowFileAccess);
+}
+
+void ExtensionPrefs::SetAllowFileAccess(const std::string& extension_id,
+ bool allow) {
+ UpdateExtensionPref(extension_id, kPrefAllowFileAccess,
+ Value::CreateBooleanValue(allow));
+ prefs_->SavePersistentPrefs();
+}
+
void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) {
const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref);
if (!dict || dict->empty())
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 3f352e7ad..77487e9 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -105,6 +105,11 @@ class ExtensionPrefs {
bool IsIncognitoEnabled(const std::string& extension_id);
void SetIsIncognitoEnabled(const std::string& extension_id, bool enabled);
+ // Returns true if the user has chosen to allow this extension to inject
+ // scripts into pages with file URLs.
+ bool AllowFileAccess(const std::string& extension_id);
+ void SetAllowFileAccess(const std::string& extension_id, bool allow);
+
// Saves ExtensionInfo for each installed extension with the path to the
// version directory and the location. Blacklisted extensions won't be saved
// and neither will external extensions the user has explicitly uninstalled.
diff --git a/chrome/browser/extensions/extension_startup_browsertest.cc b/chrome/browser/extensions/extension_startup_browsertest.cc
index ed62a40..7566caa 100644
--- a/chrome/browser/extensions/extension_startup_browsertest.cc
+++ b/chrome/browser/extensions/extension_startup_browsertest.cc
@@ -63,6 +63,7 @@ class ExtensionStartupTestBase : public InProcessBrowserTest {
if (!load_extension_.value().empty()) {
command_line->AppendSwitchWithValue(switches::kLoadExtension,
load_extension_.ToWStringHack());
+ command_line->AppendSwitch(switches::kDisableExtensionsFileAccessCheck);
}
}
@@ -143,6 +144,23 @@ IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, Test) {
TestInjection(true, true);
}
+// Tests that disallowing file access on an extension prevents it from injecting
+// script into a page with a file URL.
+IN_PROC_BROWSER_TEST_F(ExtensionsStartupTest, NoFileAccess) {
+ WaitForServicesToStart(4, true); // 1 component extension and 3 others.
+
+ ExtensionsService* service = browser()->profile()->GetExtensionsService();
+ for (size_t i = 0; i < service->extensions()->size(); ++i) {
+ if (service->AllowFileAccess(service->extensions()->at(i))) {
+ service->SetAllowFileAccess(service->extensions()->at(i), false);
+ ui_test_utils::WaitForNotification(
+ NotificationType::USER_SCRIPTS_UPDATED);
+ }
+ }
+
+ TestInjection(false, false);
+}
+
// ExtensionsLoadTest
// Ensures that we can startup the browser with --load-extension and see them
// run.
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index 71a8753..0f58360 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -646,7 +646,8 @@ bool UpdateTabFunction::RunImpl() {
// JavaScript URLs can do the same kinds of things as cross-origin XHR, so
// we need to check host permissions before allowing them.
if (url.SchemeIs(chrome::kJavaScriptScheme)) {
- if (!GetExtension()->CanExecuteScriptOnHost(contents->GetURL(), &error_))
+ if (!profile()->GetExtensionsService()->CanExecuteScriptOnHost(
+ GetExtension(), contents->GetURL(), &error_))
return false;
// TODO(aa): How does controller queue URLs? Is there any chance that this
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 99dcd9c..02a7366 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -38,6 +38,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/notification_service.h"
@@ -675,6 +676,42 @@ void ExtensionsService::SetIsIncognitoEnabled(Extension* extension,
NotifyExtensionLoaded(extension);
}
+bool ExtensionsService::AllowFileAccess(const Extension* extension) {
+ return (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableExtensionsFileAccessCheck) ||
+ extension_prefs_->AllowFileAccess(extension->id()));
+}
+
+void ExtensionsService::SetAllowFileAccess(Extension* extension, bool allow) {
+ extension_prefs_->SetAllowFileAccess(extension->id(), allow);
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
+ Source<Profile>(profile_),
+ Details<Extension>(extension));
+}
+
+bool ExtensionsService::CanExecuteScriptOnHost(Extension* extension,
+ const GURL& url,
+ std::string* error) const {
+ // No extensions are allowed to execute script on the gallery because that
+ // would allow extensions to manipulate their own install pages.
+ if (url.host() == GURL(Extension::ChromeStoreURL()).host()) {
+ if (error)
+ *error = errors::kCannotScriptGallery;
+ return false;
+ }
+
+ if (extension->HasHostPermission(url))
+ return true;
+
+ if (error) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
+ url.spec());
+ }
+
+ return false;
+}
+
void ExtensionsService::CheckForExternalUpdates() {
// This installs or updates externally provided extensions.
// TODO(aa): Why pass this list into the provider, why not just filter it
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index c7e0609..07bfa8b 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -155,6 +155,17 @@ class ExtensionsService
bool IsIncognitoEnabled(const Extension* extension);
void SetIsIncognitoEnabled(Extension* extension, bool enabled);
+ // Whether this extension can inject scripts into pages with file URLs.
+ bool AllowFileAccess(const Extension* extension);
+ void SetAllowFileAccess(Extension* extension, bool allow);
+
+ // Returns true if the extension has permission to execute script on a
+ // particular host.
+ // TODO(aa): Also use this in the renderer, for normal content script
+ // injection. Currently, that has its own copy of this code.
+ bool CanExecuteScriptOnHost(Extension* extension,
+ const GURL& url, std::string* error) const;
+
const FilePath& install_directory() const { return install_directory_; }
// Initialize and start all installed extensions.
diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc
index 9ad2e78..84a6f33c 100644
--- a/chrome/browser/extensions/extensions_ui.cc
+++ b/chrome/browser/extensions/extensions_ui.cc
@@ -125,6 +125,8 @@ void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path,
l10n_util::GetString(IDS_EXTENSIONS_ENABLE));
localized_strings.SetString(L"enableIncognito",
l10n_util::GetString(IDS_EXTENSIONS_ENABLE_INCOGNITO));
+ localized_strings.SetString(L"allowFileAccess",
+ l10n_util::GetString(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
localized_strings.SetString(L"incognitoWarning",
l10n_util::GetString(IDS_EXTENSIONS_INCOGNITO_WARNING));
localized_strings.SetString(L"reload",
@@ -282,6 +284,8 @@ void ExtensionsDOMHandler::RegisterMessages() {
NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage));
dom_ui_->RegisterMessageCallback("enableIncognito",
NewCallback(this, &ExtensionsDOMHandler::HandleEnableIncognitoMessage));
+ dom_ui_->RegisterMessageCallback("allowFileAccess",
+ NewCallback(this, &ExtensionsDOMHandler::HandleAllowFileAccessMessage));
dom_ui_->RegisterMessageCallback("uninstall",
NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage));
dom_ui_->RegisterMessageCallback("options",
@@ -493,6 +497,20 @@ void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const Value* value) {
ignore_notifications_ = false;
}
+void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const Value* value) {
+ CHECK(value->IsType(Value::TYPE_LIST));
+ const ListValue* list = static_cast<const ListValue*>(value);
+ CHECK(list->GetSize() == 2);
+ std::string extension_id, allow_str;
+ CHECK(list->GetString(0, &extension_id));
+ CHECK(list->GetString(1, &allow_str));
+ Extension* extension = extensions_service_->GetExtensionById(extension_id,
+ true);
+ DCHECK(extension);
+
+ extensions_service_->SetAllowFileAccess(extension, allow_str == "true");
+}
+
void ExtensionsDOMHandler::HandleUninstallMessage(const Value* value) {
CHECK(value->IsType(Value::TYPE_LIST));
const ListValue* list = static_cast<const ListValue*>(value);
@@ -767,9 +785,23 @@ DictionaryValue* ExtensionsDOMHandler::CreateContentScriptDetailValue(
return script_data;
}
+static bool ExtensionWantsFileAccess(const Extension* extension) {
+ for (UserScriptList::const_iterator it = extension->content_scripts().begin();
+ it != extension->content_scripts().end(); ++it) {
+ for (UserScript::PatternList::const_iterator pattern =
+ it->url_patterns().begin();
+ pattern != it->url_patterns().end(); ++pattern) {
+ if (pattern->scheme() == chrome::kFileScheme)
+ return true;
+ }
+ }
+
+ return false;
+}
+
// Static
DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
- ExtensionsService* service, const Extension *extension,
+ ExtensionsService* service, const Extension* extension,
const std::vector<ExtensionPage>& pages, bool enabled) {
DictionaryValue* extension_data = new DictionaryValue();
@@ -780,6 +812,10 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
extension_data->SetBoolean(L"enabled", enabled);
extension_data->SetBoolean(L"enabledIncognito",
service ? service->IsIncognitoEnabled(extension) : false);
+ extension_data->SetBoolean(L"wantsFileAccess",
+ ExtensionWantsFileAccess(extension));
+ extension_data->SetBoolean(L"allowFileAccess",
+ service ? service->AllowFileAccess(extension) : false);
extension_data->SetBoolean(L"allow_reload",
extension->location() == Extension::LOAD);
diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h
index e85e586..120f08a 100644
--- a/chrome/browser/extensions/extensions_ui.h
+++ b/chrome/browser/extensions/extensions_ui.h
@@ -149,6 +149,9 @@ class ExtensionsDOMHandler
// Callback for "enableIncognito" message.
void HandleEnableIncognitoMessage(const Value* value);
+ // Callback for "allowFileAcces" message.
+ void HandleAllowFileAccessMessage(const Value* value);
+
// Callback for "uninstall" message.
void HandleUninstallMessage(const Value* value);
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 608f9db..c651c2e 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -306,6 +306,8 @@ UserScriptMaster::UserScriptMaster(const FilePath& script_dir, Profile* profile)
Source<Profile>(profile_));
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
Source<Profile>(profile_));
+ registrar_.Add(this, NotificationType::EXTENSION_USER_SCRIPTS_UPDATED,
+ Source<Profile>(profile_));
}
UserScriptMaster::~UserScriptMaster() {
@@ -348,11 +350,14 @@ void UserScriptMaster::Observe(NotificationType type,
Extension* extension = Details<Extension>(details).ptr();
bool incognito_enabled = profile_->GetExtensionsService()->
IsIncognitoEnabled(extension);
+ bool allow_file_access = profile_->GetExtensionsService()->
+ AllowFileAccess(extension);
const UserScriptList& scripts = extension->content_scripts();
for (UserScriptList::const_iterator iter = scripts.begin();
iter != scripts.end(); ++iter) {
lone_scripts_.push_back(*iter);
lone_scripts_.back().set_incognito_enabled(incognito_enabled);
+ lone_scripts_.back().set_allow_file_access(allow_file_access);
}
if (extensions_service_ready_)
StartScan();
@@ -375,6 +380,23 @@ void UserScriptMaster::Observe(NotificationType type,
break;
}
+ case NotificationType::EXTENSION_USER_SCRIPTS_UPDATED: {
+ Extension* extension = Details<Extension>(details).ptr();
+ UserScriptList new_lone_scripts;
+ bool incognito_enabled = profile_->GetExtensionsService()->
+ IsIncognitoEnabled(extension);
+ bool allow_file_access = profile_->GetExtensionsService()->
+ AllowFileAccess(extension);
+ for (UserScriptList::iterator iter = lone_scripts_.begin();
+ iter != lone_scripts_.end(); ++iter) {
+ if (iter->extension_id() == extension->id()) {
+ iter->set_incognito_enabled(incognito_enabled);
+ iter->set_allow_file_access(allow_file_access);
+ }
+ }
+ StartScan();
+ break;
+ }
default:
DCHECK(false);