blob: c6619e823c4f00c41cc64c0b4e2752075793fc67 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_tab_util.h"
#include "base/json/json_reader.h"
#include "base/test/gmock_expected_support.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_util.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/mojom/context_type.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace extensions {
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForTabsPermission) {
auto extension = ExtensionBuilder("Extension with tabs permission")
.AddPermission("tabs")
.Build();
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(extension.get(),
mojom::ContextType::kUnspecified,
GURL("http://www.google.com"));
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForNoPermission) {
auto extension = ExtensionBuilder("Extension with no permissions").Build();
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(extension.get(),
mojom::ContextType::kUnspecified,
GURL("http://www.google.com"));
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully,
scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForHostPermission) {
auto extension = ExtensionBuilder("Extension with host permission")
.AddPermission("*://www.google.com/*")
.Build();
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(
extension.get(), mojom::ContextType::kUnspecified,
GURL("http://www.google.com/some/path"));
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForNoExtension) {
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(nullptr,
mojom::ContextType::kUnspecified,
GURL("http://www.google.com"));
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully,
scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForWebUI) {
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(nullptr, mojom::ContextType::kWebUi,
GURL("http://www.google.com"));
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kDontScrubTab, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ScrubTabBehaviorForWebUIUntrusted) {
ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
ExtensionTabUtil::GetScrubTabBehavior(nullptr,
mojom::ContextType::kUntrustedWebUi,
GURL("http://www.google.com"));
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully,
scrub_tab_behavior.committed_info);
EXPECT_EQ(ExtensionTabUtil::kScrubTabFully, scrub_tab_behavior.pending_info);
}
TEST(ExtensionTabUtilTest, ResolvePossiblyRelativeURL) {
auto extension = ExtensionBuilder("test").Build();
EXPECT_EQ(ExtensionTabUtil::ResolvePossiblyRelativeURL(
"http://example.com/path", extension.get()),
GURL("http://example.com/path"));
EXPECT_EQ(
ExtensionTabUtil::ResolvePossiblyRelativeURL("path", extension.get()),
GURL("chrome-extension://jpignaibiiemhngfjkcpokkamffknabf/path"));
EXPECT_EQ(ExtensionTabUtil::ResolvePossiblyRelativeURL("path", nullptr),
GURL("path"));
}
class ChromeExtensionNavigationTest : public ExtensionServiceTestBase {
public:
ChromeExtensionNavigationTest() = default;
ChromeExtensionNavigationTest(const ChromeExtensionNavigationTest&) = delete;
ChromeExtensionNavigationTest& operator=(
const ChromeExtensionNavigationTest&) = delete;
void SetUp() override;
};
void ChromeExtensionNavigationTest::SetUp() {
ExtensionServiceTestBase::SetUp();
InitializeExtensionServiceWithUpdater();
}
TEST_F(ChromeExtensionNavigationTest, PrepareURLForNavigation) {
auto extension = ExtensionBuilder("test").Build();
// A fully qualified URL should return the same URL.
{
const std::string kTestUrl("http://google.com");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kTestUrl, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(GURL(kTestUrl)));
}
// A relative path should return a URL relative to the extension's base URL.
{
const std::string kTestPath("foo");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kTestPath, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(extension->GetResourceURL(kTestPath)));
}
// A kill URL should return false and set the error. There are several
// different potential kill URLs and this just checks one of them.
{
const std::string kKillURL("chrome://crash");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kKillURL, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ErrorIs(tabs_constants::kNoCrashBrowserError));
}
// Hang URLs and other similar debug urls should also return false and set the
// error.
{
const std::string kHangURL("chrome://hang");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kHangURL, extension.get(), browser_context());
ASSERT_FALSE(url.has_value());
EXPECT_EQ(tabs_constants::kNoCrashBrowserError, url.error());
}
// JavaScript URLs should return false and set the error.
{
const std::string kJavaScriptURL("javascript:alert('foo');");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kJavaScriptURL, extension.get(), browser_context());
ASSERT_FALSE(url.has_value());
EXPECT_EQ(tabs_constants::kJavaScriptUrlsNotAllowedInExtensionNavigations,
url.error());
}
// File URLs should return false and set the error.
{
const std::string kFileURL("file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kFileURL, extension.get(), browser_context());
ASSERT_FALSE(url.has_value());
EXPECT_EQ(tabs_constants::kFileUrlsNotAllowedInExtensionNavigations,
url.error());
}
// File URLs with view-source scheme should return false and set the error.
{
const std::string kViewSourceFileURL("view-source:file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kViewSourceFileURL, extension.get(), browser_context());
ASSERT_FALSE(url.has_value());
EXPECT_EQ(tabs_constants::kFileUrlsNotAllowedInExtensionNavigations,
url.error());
}
// File URLs are returned when the extension has access to file.
{
util::SetAllowFileAccess(extension->id(), browser_context(), true);
const std::string kFileURLWithAccess("file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kFileURLWithAccess, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(GURL(kFileURLWithAccess)));
}
// Regression test for crbug.com/1487908. Ensure that file URLs are returned
// when the call originates from non-extension contexts (e.g. WebUI contexts).
{
const std::string kFileURL("file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kFileURL, /*extension=*/nullptr, browser_context());
EXPECT_THAT(url, base::test::ValueIs(GURL(kFileURL)));
}
}
TEST_F(ChromeExtensionNavigationTest,
PrepareURLForNavigationWithEnterprisePolicy) {
// Set the extension to allow file URL navigation via enterprise policy.
std::string extension_id = "abcdefghijklmnopabcdefghijklmnop";
std::string json = base::StringPrintf(
R"({
"%s": {
"file_url_navigation_allowed": true
}
})",
extension_id.c_str());
std::optional<base::Value> settings = base::JSONReader::Read(json);
testing_pref_service()->SetManagedPref(
pref_names::kExtensionManagement,
base::Value::ToUniquePtrValue(std::move(settings.value())));
auto extension = ExtensionBuilder("test").SetID(extension_id).Build();
// File URLs are returned when the extension has access to file.
const std::string kFileURLWithEnterprisePolicy("file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kFileURLWithEnterprisePolicy, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(GURL(kFileURLWithEnterprisePolicy)));
}
TEST_F(ChromeExtensionNavigationTest, PrepareURLForNavigationWithPDFViewer) {
// Set ID for PDF viewer extension.
auto extension =
ExtensionBuilder("test").SetID(extension_misc::kPdfExtensionId).Build();
// File URLs are returned when the extension has access to file.
const std::string kFileURLWithPDFViewer("file:///etc/passwd");
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kFileURLWithPDFViewer, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(GURL(kFileURLWithPDFViewer)));
}
TEST_F(ChromeExtensionNavigationTest, PrepareURLForNavigationOnDevtools) {
const std::string kDevtoolsURL(
"devtools://devtools/bundled/devtools_app.html");
// A devtools url should return false and set the error.
{
auto no_permission_extension = ExtensionBuilder("none").Build();
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kDevtoolsURL, no_permission_extension.get(), browser_context());
EXPECT_THAT(url,
base::test::ErrorIs(tabs_constants::kCannotNavigateToDevtools));
}
// Having the devtools permissions should allow access.
{
auto devtools_extension = ExtensionBuilder("devtools")
.SetManifestKey("devtools_page", "foo.html")
.Build();
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kDevtoolsURL, devtools_extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(kDevtoolsURL));
}
// Having the debugger permissions should also allow access.
{
auto debugger_extension =
ExtensionBuilder("debugger").AddPermission("debugger").Build();
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kDevtoolsURL, debugger_extension.get(), browser_context());
EXPECT_THAT(url, base::test::ValueIs(kDevtoolsURL));
}
}
TEST_F(ChromeExtensionNavigationTest,
PrepareURLForNavigationOnChromeUntrusted) {
const std::string kChromeUntrustedURL("chrome-untrusted://terminal/");
auto extension = ExtensionBuilder("none").Build();
auto url = ExtensionTabUtil::PrepareURLForNavigation(
kChromeUntrustedURL, extension.get(), browser_context());
EXPECT_THAT(url, base::test::ErrorIs(
tabs_constants::kCannotNavigateToChromeUntrusted));
}
} // namespace extensions