Commit 12962027 authored by isandrk's avatar isandrk Committed by Commit bot

PS - Filtering activeTab URL

In Public Sessions, apps and extensions are force-installed by admin policy so the user does not get a chance to review the permissions for these apps. This is not acceptable from a security standpoint, so we:
- scrub the URL available to chrome.tabs.executeScript context (through activeTab permission) down to the origin.

This change also causes the tab object passed to the [page|browser]Action.onClicked to be scrubbed for the given extension.

TEST=
  unit_tests --gtest_filter=DeviceLocalAccountManagementPolicyProviderTest.IsWhitelisted
  unit_tests --gtest_filter=ExtensionTabUtilDelegateChromeOSTest.*
  unit_tests --gtest_filter=ExtensionTabUtilTest.Delegate

BUG=717945

Review-Url: https://codereview.chromium.org/2858643002
Cr-Commit-Position: refs/heads/master@{#469342}
parent 6efb1855
...@@ -890,8 +890,8 @@ DeviceLocalAccountManagementPolicyProvider:: ...@@ -890,8 +890,8 @@ DeviceLocalAccountManagementPolicyProvider::
// static // static
bool DeviceLocalAccountManagementPolicyProvider::IsWhitelisted( bool DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
const extensions::Extension* extension) { const std::string& extension_id) {
return ArrayContains(kPublicSessionWhitelist, extension->id()); return ArrayContains(kPublicSessionWhitelist, extension_id);
} }
std::string DeviceLocalAccountManagementPolicyProvider:: std::string DeviceLocalAccountManagementPolicyProvider::
...@@ -921,7 +921,7 @@ bool DeviceLocalAccountManagementPolicyProvider::UserMayLoad( ...@@ -921,7 +921,7 @@ bool DeviceLocalAccountManagementPolicyProvider::UserMayLoad(
// Allow extension if its specific ID is whitelisted for use in public // Allow extension if its specific ID is whitelisted for use in public
// sessions. // sessions.
if (IsWhitelisted(extension)) { if (IsWhitelisted(extension->id())) {
return true; return true;
} }
......
...@@ -25,7 +25,7 @@ class DeviceLocalAccountManagementPolicyProvider ...@@ -25,7 +25,7 @@ class DeviceLocalAccountManagementPolicyProvider
~DeviceLocalAccountManagementPolicyProvider() override; ~DeviceLocalAccountManagementPolicyProvider() override;
// Used to check whether an extension is explicitly whitelisted. // Used to check whether an extension is explicitly whitelisted.
static bool IsWhitelisted(const extensions::Extension* extension); static bool IsWhitelisted(const std::string& extension_id);
// extensions::ManagementPolicy::Provider: // extensions::ManagementPolicy::Provider:
std::string GetDebugPolicyProviderName() const override; std::string GetDebugPolicyProviderName() const override;
......
...@@ -614,14 +614,12 @@ TEST(DeviceLocalAccountManagementPolicyProviderTest, KioskAppSession) { ...@@ -614,14 +614,12 @@ TEST(DeviceLocalAccountManagementPolicyProviderTest, KioskAppSession) {
TEST(DeviceLocalAccountManagementPolicyProviderTest, IsWhitelisted) { TEST(DeviceLocalAccountManagementPolicyProviderTest, IsWhitelisted) {
// Whitelisted extension, Chrome Remote Desktop. // Whitelisted extension, Chrome Remote Desktop.
auto extension = CreateRegularExtension(kWhitelistedId);
EXPECT_TRUE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted( EXPECT_TRUE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
extension.get())); kWhitelistedId));
// Bogus extension ID (never true). // Bogus extension ID (never true).
extension = CreateRegularExtension(kBogusId);
EXPECT_FALSE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted( EXPECT_FALSE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
extension.get())); kBogusId));
} }
} // namespace chromeos } // namespace chromeos
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.h"
#include "chrome/browser/profiles/profiles_state.h" #include "chrome/browser/profiles/profiles_state.h"
#include "chrome/common/extensions/api/tabs.h" #include "chrome/common/extensions/api/tabs.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -21,7 +22,9 @@ void ExtensionTabUtilDelegateChromeOS::ScrubTabForExtension( ...@@ -21,7 +22,9 @@ void ExtensionTabUtilDelegateChromeOS::ScrubTabForExtension(
const Extension* extension, const Extension* extension,
content::WebContents* contents, content::WebContents* contents,
api::tabs::Tab* tab) { api::tabs::Tab* tab) {
if (!profiles::IsPublicSession() || !tab->url) { if (!profiles::IsPublicSession() || !tab->url ||
chromeos::DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
extension->id())) {
return; return;
} }
// Scrub URL down to the origin (security reasons inside Public Sessions). // Scrub URL down to the origin (security reasons inside Public Sessions).
......
...@@ -24,7 +24,8 @@ class Extension; ...@@ -24,7 +24,8 @@ class Extension;
// In Public Sessions, apps and extensions are force-installed by admin policy // In Public Sessions, apps and extensions are force-installed by admin policy
// so the user does not get a chance to review the permissions for these apps. // so the user does not get a chance to review the permissions for these apps.
// This is not acceptable from a security standpoint, so we scrub the URL // This is not acceptable from a security standpoint, so we scrub the URL
// returned by chrome.tabs API down to the origin. // returned by chrome.tabs API down to the origin unless the extension ID is
// whitelisted.
class ExtensionTabUtilDelegateChromeOS : public ExtensionTabUtil::Delegate { class ExtensionTabUtilDelegateChromeOS : public ExtensionTabUtil::Delegate {
public: public:
ExtensionTabUtilDelegateChromeOS(); ExtensionTabUtilDelegateChromeOS();
......
...@@ -7,15 +7,31 @@ ...@@ -7,15 +7,31 @@
#include <string> #include <string>
#include "chromeos/login/login_state.h" #include "chromeos/login/login_state.h"
#include "chromeos/login/scoped_test_public_session_login_state.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/value_builder.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace extensions { namespace extensions {
namespace { namespace {
const char kWhitelistedId[] = "cbkkbcmdlboombapidmoeolnmdacpkch";
// Use an extension ID that will never be whitelisted.
const char kNonWhitelistedId[] = "bogus";
const char kTestUrl[] = "http://www.foo.bar/baz?key=val"; const char kTestUrl[] = "http://www.foo.bar/baz?key=val";
const char kFilteredUrl[] = "http://www.foo.bar/"; const char kFilteredUrl[] = "http://www.foo.bar/";
scoped_refptr<Extension> CreateExtension(const std::string& id) {
return ExtensionBuilder()
.SetManifest(
DictionaryBuilder().Set("name", "test").Set("version", "0.1").Build())
.SetID(id)
.Build();
}
} // namespace } // namespace
class ExtensionTabUtilDelegateChromeOSTest : public testing::Test { class ExtensionTabUtilDelegateChromeOSTest : public testing::Test {
...@@ -30,25 +46,40 @@ void ExtensionTabUtilDelegateChromeOSTest::SetUp() { ...@@ -30,25 +46,40 @@ void ExtensionTabUtilDelegateChromeOSTest::SetUp() {
tab_.url.reset(new std::string(kTestUrl)); tab_.url.reset(new std::string(kTestUrl));
} }
TEST_F(ExtensionTabUtilDelegateChromeOSTest, NoFilteringOutsidePublicSession) { TEST_F(ExtensionTabUtilDelegateChromeOSTest,
NoFilteringOutsidePublicSessionForWhitelisted) {
ASSERT_FALSE(chromeos::LoginState::IsInitialized()); ASSERT_FALSE(chromeos::LoginState::IsInitialized());
delegate_.ScrubTabForExtension(nullptr, nullptr, &tab_); auto extension = CreateExtension(kWhitelistedId);
delegate_.ScrubTabForExtension(extension.get(), nullptr, &tab_);
EXPECT_EQ(kTestUrl, *tab_.url); EXPECT_EQ(kTestUrl, *tab_.url);
} }
TEST_F(ExtensionTabUtilDelegateChromeOSTest, ScrubURL) { TEST_F(ExtensionTabUtilDelegateChromeOSTest,
// Set Public Session state. NoFilteringOutsidePublicSessionForNonWhitelisted) {
chromeos::LoginState::Initialize(); ASSERT_FALSE(chromeos::LoginState::IsInitialized());
chromeos::LoginState::Get()->SetLoggedInState(
chromeos::LoginState::LOGGED_IN_ACTIVE,
chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT);
delegate_.ScrubTabForExtension(nullptr, nullptr, &tab_); auto extension = CreateExtension(kNonWhitelistedId);
EXPECT_EQ(kFilteredUrl, *tab_.url); delegate_.ScrubTabForExtension(extension.get(), nullptr, &tab_);
EXPECT_EQ(kTestUrl, *tab_.url);
}
// Reset state at the end of test. TEST_F(ExtensionTabUtilDelegateChromeOSTest,
chromeos::LoginState::Shutdown(); NoFilteringInsidePublicSessionForWhitelisted) {
chromeos::ScopedTestPublicSessionLoginState state;
auto extension = CreateExtension(kWhitelistedId);
delegate_.ScrubTabForExtension(extension.get(), nullptr, &tab_);
EXPECT_EQ(kTestUrl, *tab_.url);
}
TEST_F(ExtensionTabUtilDelegateChromeOSTest,
FilterInsidePublicSessionNonWhitelisted) {
chromeos::ScopedTestPublicSessionLoginState state;
auto extension = CreateExtension(kNonWhitelistedId);
delegate_.ScrubTabForExtension(extension.get(), nullptr, &tab_);
EXPECT_EQ(kFilteredUrl, *tab_.url);
} }
} // namespace extensions } // namespace extensions
...@@ -23,7 +23,7 @@ void PermissionsUpdaterDelegateChromeOS::InitializePermissions( ...@@ -23,7 +23,7 @@ void PermissionsUpdaterDelegateChromeOS::InitializePermissions(
std::unique_ptr<const PermissionSet>* granted_permissions) { std::unique_ptr<const PermissionSet>* granted_permissions) {
if (!profiles::IsPublicSession() || if (!profiles::IsPublicSession() ||
chromeos::DeviceLocalAccountManagementPolicyProvider::IsWhitelisted( chromeos::DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
extension) || extension->id()) ||
!(*granted_permissions) !(*granted_permissions)
->HasAPIPermission(APIPermission::kClipboardRead)) { ->HasAPIPermission(APIPermission::kClipboardRead)) {
return; return;
......
...@@ -205,7 +205,8 @@ void ExtensionActionAPI::NotifyChange(ExtensionAction* extension_action, ...@@ -205,7 +205,8 @@ void ExtensionActionAPI::NotifyChange(ExtensionAction* extension_action,
void ExtensionActionAPI::DispatchExtensionActionClicked( void ExtensionActionAPI::DispatchExtensionActionClicked(
const ExtensionAction& extension_action, const ExtensionAction& extension_action,
WebContents* web_contents) { WebContents* web_contents,
const Extension* extension) {
events::HistogramValue histogram_value = events::UNKNOWN; events::HistogramValue histogram_value = events::UNKNOWN;
const char* event_name = NULL; const char* event_name = NULL;
switch (extension_action.action_type()) { switch (extension_action.action_type()) {
...@@ -225,7 +226,8 @@ void ExtensionActionAPI::DispatchExtensionActionClicked( ...@@ -225,7 +226,8 @@ void ExtensionActionAPI::DispatchExtensionActionClicked(
if (event_name) { if (event_name) {
std::unique_ptr<base::ListValue> args(new base::ListValue()); std::unique_ptr<base::ListValue> args(new base::ListValue());
args->Append(ExtensionTabUtil::CreateTabObject(web_contents)->ToValue()); args->Append(
ExtensionTabUtil::CreateTabObject(web_contents, extension)->ToValue());
DispatchEventToExtension(web_contents->GetBrowserContext(), DispatchEventToExtension(web_contents->GetBrowserContext(),
extension_action.extension_id(), histogram_value, extension_action.extension_id(), histogram_value,
......
...@@ -93,7 +93,8 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI { ...@@ -93,7 +93,8 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI {
// Dispatches the onClicked event for extension that owns the given action. // Dispatches the onClicked event for extension that owns the given action.
void DispatchExtensionActionClicked(const ExtensionAction& extension_action, void DispatchExtensionActionClicked(const ExtensionAction& extension_action,
content::WebContents* web_contents); content::WebContents* web_contents,
const Extension* extension);
// Clears the values for all ExtensionActions for the tab associated with the // Clears the values for all ExtensionActions for the tab associated with the
// given |web_contents| (and signals that page actions changed). // given |web_contents| (and signals that page actions changed).
......
...@@ -120,7 +120,8 @@ ExtensionAction::ShowAction ExtensionActionRunner::RunAction( ...@@ -120,7 +120,8 @@ ExtensionAction::ShowAction ExtensionActionRunner::RunAction(
return ExtensionAction::ACTION_SHOW_POPUP; return ExtensionAction::ACTION_SHOW_POPUP;
ExtensionActionAPI::Get(browser_context_) ExtensionActionAPI::Get(browser_context_)
->DispatchExtensionActionClicked(*extension_action, web_contents()); ->DispatchExtensionActionClicked(*extension_action, web_contents(),
extension);
return ExtensionAction::ACTION_NONE; return ExtensionAction::ACTION_NONE;
} }
......
...@@ -111,7 +111,7 @@ IN_PROC_BROWSER_TEST_F(NativeBindingsApiTest, DeclarativeEvents) { ...@@ -111,7 +111,7 @@ IN_PROC_BROWSER_TEST_F(NativeBindingsApiTest, DeclarativeEvents) {
// And the extension should be notified of the click. // And the extension should be notified of the click.
ExtensionTestMessageListener clicked_listener("clicked and removed", false); ExtensionTestMessageListener clicked_listener("clicked and removed", false);
ExtensionActionAPI::Get(profile())->DispatchExtensionActionClicked( ExtensionActionAPI::Get(profile())->DispatchExtensionActionClicked(
*page_action, web_contents); *page_action, web_contents, extension);
ASSERT_TRUE(clicked_listener.WaitUntilSatisfied()); ASSERT_TRUE(clicked_listener.WaitUntilSatisfied());
} }
......
...@@ -548,6 +548,8 @@ static_library("test_support") { ...@@ -548,6 +548,8 @@ static_library("test_support") {
"login/auth/mock_auth_status_consumer.h", "login/auth/mock_auth_status_consumer.h",
"login/auth/mock_url_fetchers.cc", "login/auth/mock_url_fetchers.cc",
"login/auth/mock_url_fetchers.h", "login/auth/mock_url_fetchers.h",
"login/scoped_test_public_session_login_state.cc",
"login/scoped_test_public_session_login_state.h",
"network/fake_network_device_handler.cc", "network/fake_network_device_handler.cc",
"network/fake_network_device_handler.h", "network/fake_network_device_handler.h",
"network/mock_managed_network_configuration_handler.cc", "network/mock_managed_network_configuration_handler.cc",
......
// Copyright 2017 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 "chromeos/login/scoped_test_public_session_login_state.h"
#include "chromeos/login/login_state.h"
namespace chromeos {
ScopedTestPublicSessionLoginState::ScopedTestPublicSessionLoginState() {
// Set Public Session state.
chromeos::LoginState::Initialize();
chromeos::LoginState::Get()->SetLoggedInState(
chromeos::LoginState::LOGGED_IN_ACTIVE,
chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT);
}
ScopedTestPublicSessionLoginState::~ScopedTestPublicSessionLoginState() {
// Reset state at the end of test.
chromeos::LoginState::Shutdown();
}
} // namespace chromeos
// Copyright 2017 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 CHROMEOS_LOGIN_SCOPED_TEST_PUBLIC_SESSION_LOGIN_STATE_H_
#define CHROMEOS_LOGIN_SCOPED_TEST_PUBLIC_SESSION_LOGIN_STATE_H_
#include "base/macros.h"
namespace chromeos {
// A class to start and shutdown public session state for a test.
class ScopedTestPublicSessionLoginState {
public:
ScopedTestPublicSessionLoginState();
~ScopedTestPublicSessionLoginState();
private:
DISALLOW_COPY_AND_ASSIGN(ScopedTestPublicSessionLoginState);
};
} // namespace chromeos
#endif // CHROMEOS_LOGIN_SCOPED_TEST_PUBLIC_SESSION_LOGIN_STATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment