blob: f94bfdf579a577106b3de6181b255c6882c58a29 [file]
// Copyright 2019 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/file_system_access/chrome_file_system_access_permission_context.h"
#include <memory>
#include <string>
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_path_override.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
using content::BrowserContext;
using HandleType = ChromeFileSystemAccessPermissionContext::HandleType;
using PathType = ChromeFileSystemAccessPermissionContext::PathType;
using UserAction = ChromeFileSystemAccessPermissionContext::UserAction;
using PermissionStatus =
content::FileSystemAccessPermissionGrant::PermissionStatus;
using PermissionRequestOutcome =
content::FileSystemAccessPermissionGrant::PermissionRequestOutcome;
using SensitiveDirectoryResult =
ChromeFileSystemAccessPermissionContext::SensitiveDirectoryResult;
class TestFileSystemAccessPermissionContext
: public ChromeFileSystemAccessPermissionContext {
public:
explicit TestFileSystemAccessPermissionContext(
content::BrowserContext* context)
: ChromeFileSystemAccessPermissionContext(context) {}
~TestFileSystemAccessPermissionContext() override = default;
// content::FileSystemAccessPermissionContext:
scoped_refptr<content::FileSystemAccessPermissionGrant>
GetReadPermissionGrant(const url::Origin& origin,
const base::FilePath& path,
HandleType handle_type,
UserAction user_action) override {
NOTREACHED();
return nullptr;
}
scoped_refptr<content::FileSystemAccessPermissionGrant>
GetWritePermissionGrant(const url::Origin& origin,
const base::FilePath& path,
HandleType handle_type,
UserAction user_action) override {
NOTREACHED();
return nullptr;
}
// ChromeFileSystemAccessPermissionContext:
Grants GetPermissionGrants(const url::Origin& origin) override {
NOTREACHED();
return {};
}
void RevokeGrants(const url::Origin& origin) override { NOTREACHED(); }
private:
base::WeakPtr<ChromeFileSystemAccessPermissionContext> GetWeakPtr() override {
return weak_factory_.GetWeakPtr();
}
base::WeakPtrFactory<TestFileSystemAccessPermissionContext> weak_factory_{
this};
};
class ChromeFileSystemAccessPermissionContextTest : public testing::Test {
public:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
permission_context_ =
std::make_unique<TestFileSystemAccessPermissionContext>(
browser_context());
}
void TearDown() override { ASSERT_TRUE(temp_dir_.Delete()); }
SensitiveDirectoryResult ConfirmSensitiveDirectoryAccessSync(
ChromeFileSystemAccessPermissionContext* context,
PathType path_type,
const base::FilePath& path,
HandleType handle_type) {
base::RunLoop loop;
SensitiveDirectoryResult out_result;
permission_context_->ConfirmSensitiveDirectoryAccess(
kTestOrigin, path_type, path, handle_type,
content::GlobalFrameRoutingId(),
base::BindLambdaForTesting([&](SensitiveDirectoryResult result) {
out_result = result;
loop.Quit();
}));
loop.Run();
return out_result;
}
void SetDefaultContentSettingValue(ContentSettingsType type,
ContentSetting value) {
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(&profile_);
content_settings->SetDefaultContentSetting(type, value);
}
void SetContentSettingValueForOrigin(url::Origin origin,
ContentSettingsType type,
ContentSetting value) {
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(&profile_);
content_settings->SetContentSettingDefaultScope(
origin.GetURL(), origin.GetURL(), type, value);
}
ChromeFileSystemAccessPermissionContext* permission_context() {
return permission_context_.get();
}
BrowserContext* browser_context() { return &profile_; }
TestingProfile* profile() { return &profile_; }
protected:
const url::Origin kTestOrigin =
url::Origin::Create(GURL("https://example.com"));
const url::Origin kTestOrigin2 =
url::Origin::Create(GURL("https://test.com"));
const base::FilePath kTestPath =
base::FilePath(FILE_PATH_LITERAL("/foo/bar"));
const url::Origin kChromeOrigin = url::Origin::Create(GURL("chrome://test"));
content::BrowserTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<ChromeFileSystemAccessPermissionContext> permission_context_;
TestingProfile profile_;
};
#if !defined(OS_ANDROID)
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_NoSpecialPath) {
const base::FilePath kTestPath =
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
base::FilePath(FILE_PATH_LITERAL("c:\\foo\\bar"));
#else
base::FilePath(FILE_PATH_LITERAL("/foo/bar"));
#endif
// Path outside any special directories should be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, kTestPath,
HandleType::kFile));
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, kTestPath,
HandleType::kDirectory));
// External (relative) paths should also be allowed.
EXPECT_EQ(
SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kExternal,
base::FilePath(FILE_PATH_LITERAL("foo/bar")), HandleType::kFile));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_DontBlockAllChildren) {
base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
base::ScopedPathOverride home_override(base::DIR_HOME, home_dir, true, true);
// Home directory itself should not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, home_dir,
HandleType::kDirectory));
// Parent of home directory should also not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal, temp_dir_.GetPath(),
HandleType::kDirectory));
// Paths inside home directory should be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
home_dir.AppendASCII("foo"), HandleType::kFile));
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
home_dir.AppendASCII("foo"), HandleType::kDirectory));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_BlockAllChildren) {
base::FilePath app_dir = temp_dir_.GetPath().AppendASCII("app");
base::ScopedPathOverride app_override(chrome::DIR_APP, app_dir, true, true);
// App directory itself should not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, app_dir,
HandleType::kDirectory));
// Parent of App directory should also not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal, temp_dir_.GetPath(),
HandleType::kDirectory));
// Paths inside App directory should also not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
app_dir.AppendASCII("foo"), HandleType::kFile));
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
app_dir.AppendASCII("foo"), HandleType::kDirectory));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_BlockChildrenNested) {
base::FilePath user_data_dir = temp_dir_.GetPath().AppendASCII("user");
base::ScopedPathOverride user_data_override(chrome::DIR_USER_DATA,
user_data_dir, true, true);
base::FilePath download_dir = user_data_dir.AppendASCII("downloads");
base::ScopedPathOverride download_override(chrome::DIR_DEFAULT_DOWNLOADS,
download_dir, true, true);
// User Data directory itself should not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, user_data_dir,
HandleType::kDirectory));
// Parent of User Data directory should also not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal, temp_dir_.GetPath(),
HandleType::kDirectory));
// The nested Download directory itself should not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(permission_context(),
PathType::kLocal, download_dir,
HandleType::kDirectory));
// Paths inside the nested Download directory should be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
download_dir.AppendASCII("foo"), HandleType::kFile));
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
download_dir.AppendASCII("foo"), HandleType::kDirectory));
#if defined(OS_WIN)
// DIR_IE_INTERNET_CACHE is an example of a directory where nested directories
// are blocked, but nested files should be allowed.
base::FilePath internet_cache = user_data_dir.AppendASCII("INetCache");
base::ScopedPathOverride internet_cache_override(base::DIR_IE_INTERNET_CACHE,
internet_cache, true, true);
// The nested INetCache directory itself should not be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal, internet_cache,
HandleType::kDirectory));
// Files inside the nested INetCache directory should be allowed.
EXPECT_EQ(SensitiveDirectoryResult::kAllowed,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
internet_cache.AppendASCII("foo"), HandleType::kFile));
// But directories should be blocked.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
internet_cache.AppendASCII("foo"), HandleType::kDirectory));
#endif
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_RelativePathBlock) {
base::FilePath home_dir = temp_dir_.GetPath().AppendASCII("home");
base::ScopedPathOverride home_override(base::DIR_HOME, home_dir, true, true);
// ~/.ssh should be blocked
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
home_dir.AppendASCII(".ssh"), HandleType::kDirectory));
// And anything inside ~/.ssh should also be blocked
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
home_dir.AppendASCII(".ssh/id_rsa"), HandleType::kFile));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
ConfirmSensitiveDirectoryAccess_ExplicitPathBlock) {
// Linux is the only OS where we have some blocked directories with explicit
// paths (as opposed to PathService provided paths).
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// /dev should be blocked.
EXPECT_EQ(
SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
base::FilePath(FILE_PATH_LITERAL("/dev")), HandleType::kDirectory));
// As well as children of /dev.
EXPECT_EQ(SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
base::FilePath(FILE_PATH_LITERAL("/dev/foo")),
HandleType::kDirectory));
EXPECT_EQ(
SensitiveDirectoryResult::kAbort,
ConfirmSensitiveDirectoryAccessSync(
permission_context(), PathType::kLocal,
base::FilePath(FILE_PATH_LITERAL("/dev/foo")), HandleType::kFile));
#endif
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
CanObtainWritePermission_ContentSettingAsk) {
SetDefaultContentSettingValue(ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
CONTENT_SETTING_ASK);
EXPECT_TRUE(permission_context()->CanObtainWritePermission(kTestOrigin));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
CanObtainWritePermission_ContentSettingsBlock) {
SetDefaultContentSettingValue(ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
CONTENT_SETTING_BLOCK);
EXPECT_FALSE(permission_context()->CanObtainWritePermission(kTestOrigin));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
CanObtainWritePermission_ContentSettingAllow) {
// Note, chrome:// scheme is whitelisted. But we can't set default content
// setting here because ALLOW is not an acceptable option.
EXPECT_TRUE(permission_context()->CanObtainWritePermission(kChromeOrigin));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, PolicyReadGuardPermission) {
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedDefaultFileSystemReadGuardSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
EXPECT_FALSE(permission_context()->CanObtainReadPermission(kTestOrigin));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
PolicyWriteGuardPermission) {
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedDefaultFileSystemWriteGuardSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
EXPECT_FALSE(permission_context()->CanObtainWritePermission(kTestOrigin));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, PolicyReadAskForUrls) {
// Set the default to "block" so that the policy being tested overrides it.
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedDefaultFileSystemReadGuardSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
prefs->SetManagedPref(prefs::kManagedFileSystemReadAskForUrls,
base::JSONReader::ReadDeprecated(
"[\"" + kTestOrigin.Serialize() + "\"]"));
EXPECT_TRUE(permission_context()->CanObtainReadPermission(kTestOrigin));
EXPECT_FALSE(permission_context()->CanObtainReadPermission(kTestOrigin2));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, PolicyReadBlockedForUrls) {
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedFileSystemReadBlockedForUrls,
base::JSONReader::ReadDeprecated(
"[\"" + kTestOrigin.Serialize() + "\"]"));
EXPECT_FALSE(permission_context()->CanObtainReadPermission(kTestOrigin));
EXPECT_TRUE(permission_context()->CanObtainReadPermission(kTestOrigin2));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, PolicyWriteAskForUrls) {
// Set the default to "block" so that the policy being tested overrides it.
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedDefaultFileSystemWriteGuardSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
prefs->SetManagedPref(prefs::kManagedFileSystemWriteAskForUrls,
base::JSONReader::ReadDeprecated(
"[\"" + kTestOrigin.Serialize() + "\"]"));
EXPECT_TRUE(permission_context()->CanObtainWritePermission(kTestOrigin));
EXPECT_FALSE(permission_context()->CanObtainWritePermission(kTestOrigin2));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, PolicyWriteBlockedForUrls) {
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedFileSystemWriteBlockedForUrls,
base::JSONReader::ReadDeprecated(
"[\"" + kTestOrigin.Serialize() + "\"]"));
EXPECT_FALSE(permission_context()->CanObtainWritePermission(kTestOrigin));
EXPECT_TRUE(permission_context()->CanObtainWritePermission(kTestOrigin2));
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, GetLastPickedDirectory) {
auto file_info = permission_context()->GetLastPickedDirectory(kTestOrigin);
EXPECT_EQ(file_info.path, base::FilePath());
EXPECT_EQ(file_info.type, PathType::kLocal);
}
TEST_F(ChromeFileSystemAccessPermissionContextTest, SetLastPickedDirectory) {
EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
base::FilePath());
auto type = PathType::kLocal;
permission_context()->SetLastPickedDirectory(kTestOrigin, kTestPath, type);
auto path_info = permission_context()->GetLastPickedDirectory(kTestOrigin);
EXPECT_EQ(path_info.path, kTestPath);
EXPECT_EQ(path_info.type, type);
auto new_path = path_info.path.AppendASCII("baz");
auto new_type = PathType::kExternal;
permission_context()->SetLastPickedDirectory(kTestOrigin, new_path, new_type);
auto new_path_info =
permission_context()->GetLastPickedDirectory(kTestOrigin);
EXPECT_EQ(new_path_info.path, new_path);
EXPECT_EQ(new_path_info.type, new_type);
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
SetLastPickedDirectory_NewPermissionContext) {
EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
base::FilePath());
const base::FilePath path = base::FilePath(FILE_PATH_LITERAL("/baz/bar"));
permission_context()->SetLastPickedDirectory(kTestOrigin, path,
PathType::kLocal);
ASSERT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
path);
TestFileSystemAccessPermissionContext new_permission_context(
browser_context());
EXPECT_EQ(new_permission_context.GetLastPickedDirectory(kTestOrigin).path,
path);
auto new_path = path.AppendASCII("foo");
new_permission_context.SetLastPickedDirectory(kTestOrigin, new_path,
PathType::kLocal);
EXPECT_EQ(permission_context()->GetLastPickedDirectory(kTestOrigin).path,
new_path);
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
GetCommonDirectoryPath_Base_OK) {
base::ScopedPathOverride user_desktop_override(
base::DIR_USER_DESKTOP, temp_dir_.GetPath(), true, true);
EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
blink::mojom::CommonDirectory::kDirDesktop),
temp_dir_.GetPath());
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
GetCommonDirectoryPath_Chrome_OK) {
base::ScopedPathOverride user_documents_override(
chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
blink::mojom::CommonDirectory::kDirDocuments),
temp_dir_.GetPath());
}
TEST_F(ChromeFileSystemAccessPermissionContextTest,
GetCommonDirectoryPath_Default) {
base::ScopedPathOverride user_documents_override(
chrome::DIR_USER_DOCUMENTS, temp_dir_.GetPath(), true, true);
EXPECT_EQ(permission_context_->GetCommonDirectoryPath(
blink::mojom::CommonDirectory::kDefault),
temp_dir_.GetPath());
}
#endif // !defined(OS_ANDROID)