blob: 33d0029ff4c5a6ca7e2f013cc7fe0df24616d952 [file] [log] [blame]
// Copyright 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.
#include "chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.h"
#include <memory>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string16.h"
#include "base/values.h"
#include "chromeos/login/login_state/scoped_test_public_session_login_state.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace {
const char kWhitelistedId[] = "cbkkbcmdlboombapidmoeolnmdacpkch";
const char kBogusId[] = "bogus";
scoped_refptr<const extensions::Extension> CreateExtensionFromValues(
const std::string& id,
extensions::Manifest::Location location,
base::DictionaryValue* values,
int flags) {
values->SetString(extensions::manifest_keys::kName, "test");
values->SetString(extensions::manifest_keys::kVersion, "0.1");
values->SetInteger(extensions::manifest_keys::kManifestVersion, 2);
std::string error;
return extensions::Extension::Create(base::FilePath(),
location,
*values,
flags,
id,
&error);
}
scoped_refptr<const extensions::Extension> CreateRegularExtension(
const std::string& id) {
base::DictionaryValue values;
return CreateExtensionFromValues(id,
extensions::Manifest::INTERNAL,
&values,
extensions::Extension::NO_FLAGS);
}
scoped_refptr<const extensions::Extension> CreateExternalComponentExtension() {
base::DictionaryValue values;
return CreateExtensionFromValues(std::string(),
extensions::Manifest::EXTERNAL_COMPONENT,
&values,
extensions::Extension::NO_FLAGS);
}
scoped_refptr<const extensions::Extension> CreateHostedApp() {
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kApp,
std::make_unique<base::DictionaryValue>());
values.Set(extensions::manifest_keys::kWebURLs,
std::make_unique<base::ListValue>());
return CreateExtensionFromValues(std::string(),
extensions::Manifest::INTERNAL,
&values,
extensions::Extension::NO_FLAGS);
}
scoped_refptr<const extensions::Extension> CreatePlatformAppWithExtraValues(
const base::DictionaryValue* extra_values,
extensions::Manifest::Location location,
int flags) {
base::DictionaryValue values;
values.SetString("app.background.page", "background.html");
values.MergeDictionary(extra_values);
return CreateExtensionFromValues(std::string(), location, &values, flags);
}
scoped_refptr<const extensions::Extension> CreatePlatformApp() {
base::DictionaryValue values;
return CreatePlatformAppWithExtraValues(&values,
extensions::Manifest::INTERNAL,
extensions::Extension::NO_FLAGS);
}
} // namespace
TEST(DeviceLocalAccountManagementPolicyProviderTest, PublicSession) {
DeviceLocalAccountManagementPolicyProvider
provider(policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION);
// Set the login state to a public session.
ScopedTestPublicSessionLoginState login_state;
// Verify that if an extension's location has been whitelisted for use in
// public sessions, the extension can be installed.
scoped_refptr<const extensions::Extension> extension =
CreateExternalComponentExtension();
ASSERT_TRUE(extension.get());
base::string16 error;
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
// Verify that if an extension's type has been whitelisted for use in
// device-local accounts, the extension can be installed.
extension = CreateHostedApp();
ASSERT_TRUE(extension.get());
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
// Verify that if an extension's ID has been explicitly whitelisted for use in
// device-local accounts, the extension can be installed.
extension = CreateRegularExtension(kWhitelistedId);
ASSERT_TRUE(extension.get());
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
// Verify that if neither the location, type nor the ID of an extension have
// been whitelisted for use in public sessions, the extension cannot be
// installed.
extension = CreateRegularExtension(std::string());
ASSERT_TRUE(extension.get());
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
// Verify that a minimal platform app can be installed from location
// EXTERNAL_POLICY.
{
base::DictionaryValue values;
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a minimal platform app can be installed from location
// EXTERNAL_POLICY_DOWNLOAD.
{
base::DictionaryValue values;
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a minimal platform app cannot be installed from location
// UNPACKED.
{
base::DictionaryValue values;
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::UNPACKED,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with all safe manifest entries can be installed.
{
base::DictionaryValue values;
values.SetString(extensions::manifest_keys::kDescription, "something");
values.SetString(extensions::manifest_keys::kShortName, "something else");
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("alarms");
permissions->AppendString("background");
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
auto optional_permissions = std::make_unique<base::ListValue>();
optional_permissions->AppendString("alarms");
optional_permissions->AppendString("background");
values.Set(extensions::manifest_keys::kOptionalPermissions,
std::move(optional_permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unknown manifest entry cannot be
// installed.
{
base::DictionaryValue values;
values.SetString("not_whitelisted", "something");
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unsafe manifest entry cannot be
// installed. Since the program logic is based entirely on whitelists, there
// is no significant advantage in testing all unsafe manifest entries
// individually.
{
base::DictionaryValue values;
values.Set("chrome_settings_overrides",
std::make_unique<base::DictionaryValue>());
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unknown manifest entry under "app"
// cannot be installed.
{
base::DictionaryValue values;
values.SetString("app.not_whitelisted2", "something2");
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with a safe manifest entry under "app" can be
// installed.
{
base::DictionaryValue values;
values.SetString("app.content_security_policy", "something2");
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a hosted app with a safe manifest entry under "app" can be
// installed.
{
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kApp,
std::make_unique<base::DictionaryValue>());
values.Set(extensions::manifest_keys::kWebURLs,
std::make_unique<base::ListValue>());
values.SetString("app.content_security_policy", "something2");
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a theme with a safe manifest entry under "app" cannot be
// installed.
{
base::DictionaryValue values;
values.Set("theme", std::make_unique<base::DictionaryValue>());
values.SetString("app.content_security_policy", "something2");
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unknown permission entry cannot be
// installed.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("not_whitelisted_permission");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unsafe permission entry cannot be
// installed. Since the program logic is based entirely on whitelists, there
// is no significant advantage in testing all unsafe permissions individually.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("experimental");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an unsafe optional permission entry cannot
// be installed.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("experimental");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kOptionalPermissions,
std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with an url_handlers manifest entry and which is
// not installed through the web store cannot be installed.
{
auto matches = std::make_unique<base::ListValue>();
matches->AppendString("https://example.com/*");
base::DictionaryValue values;
values.Set("url_handlers.example_com.matches", std::move(matches));
values.SetString("url_handlers.example_com.title", "example title");
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with a url_handlers manifest entry and which is
// installed through the web store can be installed.
{
auto matches = std::make_unique<base::ListValue>();
matches->AppendString("https://example.com/*");
base::DictionaryValue values;
values.Set("url_handlers.example_com.matches", std::move(matches));
values.SetString("url_handlers.example_com.title", "example title");
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::FROM_WEBSTORE);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a platform app with remote URL permissions can be installed.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("https://example.com/");
permissions->AppendString("http://example.com/");
permissions->AppendString("ftp://example.com/");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that an extension with remote URL permissions cannot be installed.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("https://example.com/");
permissions->AppendString("http://example.com/");
permissions->AppendString("ftp://example.com/");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with a local URL permission cannot be installed.
{
auto permissions = std::make_unique<base::ListValue>();
permissions->AppendString("file:///some/where");
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that a platform app with socket dictionary permission can be
// installed.
{
auto socket = std::make_unique<base::DictionaryValue>();
auto tcp_list = std::make_unique<base::ListValue>();
tcp_list->AppendString("tcp-connect");
socket->Set("socket", std::move(tcp_list));
auto permissions = std::make_unique<base::ListValue>();
permissions->Append(std::move(socket));
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a platform app with unknown dictionary permission cannot be
// installed.
{
auto socket = std::make_unique<base::DictionaryValue>();
auto tcp_list = std::make_unique<base::ListValue>();
tcp_list->AppendString("unknown_value");
socket->Set("unknown_key", std::move(tcp_list));
auto permissions = std::make_unique<base::ListValue>();
permissions->Append(std::move(socket));
base::DictionaryValue values;
values.Set(extensions::manifest_keys::kPermissions, std::move(permissions));
extension = CreatePlatformAppWithExtraValues(
&values,
extensions::Manifest::EXTERNAL_POLICY,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
// Verify that an extension can be installed.
{
base::DictionaryValue values;
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a shared_module can be installed.
{
base::DictionaryValue values;
values.Set("export.whitelist", std::make_unique<base::ListValue>());
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a theme can be installed.
{
base::DictionaryValue values;
values.Set("theme", std::make_unique<base::DictionaryValue>());
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
// Verify that a legacy_packaged_app cannot be installed and that it cannot
// have an "app" manifest entry.
{
base::DictionaryValue values;
values.SetString("app.launch.local_path", "something");
extension = CreateExtensionFromValues(
std::string(),
extensions::Manifest::EXTERNAL_POLICY,
&values,
extensions::Extension::NO_FLAGS);
ASSERT_TRUE(extension);
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
}
}
TEST(DeviceLocalAccountManagementPolicyProviderTest, KioskAppSession) {
DeviceLocalAccountManagementPolicyProvider
provider(policy::DeviceLocalAccount::TYPE_KIOSK_APP);
// Verify that a platform app can be installed.
scoped_refptr<const extensions::Extension> extension = CreatePlatformApp();
ASSERT_TRUE(extension.get());
base::string16 error;
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
// Verify that an extension whose location has been whitelisted for use in
// other types of device-local accounts cannot be installed in a single-app
// kiosk session.
extension = CreateExternalComponentExtension();
ASSERT_TRUE(extension.get());
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
// Verify that an extension whose type has been whitelisted for use in other
// types of device-local accounts cannot be installed in a single-app kiosk
// session.
extension = CreateHostedApp();
ASSERT_TRUE(extension.get());
EXPECT_FALSE(provider.UserMayLoad(extension.get(), &error));
EXPECT_NE(base::string16(), error);
error.clear();
// Verify that an extension whose ID has been whitelisted for use in other
// types of device-local accounts cannot be installed in a single-app kiosk
// session.
extension = CreateRegularExtension(kWhitelistedId);
ASSERT_TRUE(extension.get());
EXPECT_TRUE(provider.UserMayLoad(extension.get(), &error));
EXPECT_EQ(base::string16(), error);
error.clear();
}
TEST(DeviceLocalAccountManagementPolicyProviderTest, IsWhitelisted) {
// Whitelisted extension, Chrome Remote Desktop.
EXPECT_TRUE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
kWhitelistedId));
// Bogus extension ID (never true).
EXPECT_FALSE(DeviceLocalAccountManagementPolicyProvider::IsWhitelisted(
kBogusId));
}
} // namespace chromeos