blob: e19d2b0d66c30be80a57bf78171a07f68ae8b340 [file] [log] [blame]
// Copyright (c) 2012 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/renderer/chrome_content_renderer_client.h"
#include <stddef.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/renderer/searchbox/search_bouncer.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/webplugininfo.h"
#include "extensions/buildflags/buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/manifest_constants.h"
#endif
#if BUILDFLAG(ENABLE_NACL)
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#endif
#if BUILDFLAG(ENABLE_NACL)
using blink::WebPluginParams;
using blink::WebString;
using blink::WebVector;
#endif
using content::WebPluginInfo;
using content::WebPluginMimeType;
namespace {
#if BUILDFLAG(ENABLE_NACL)
const bool kNaClRestricted = false;
const bool kNaClUnrestricted = true;
const bool kExtensionNotFromWebStore = false;
const bool kExtensionFromWebStore = true;
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
const bool kNotHostedApp = false;
const bool kHostedApp = true;
#endif
#if BUILDFLAG(ENABLE_NACL)
const char kExtensionUrl[] = "chrome-extension://extension_id/background.html";
const char kChatManifestFS[] = "filesystem:https://talkgadget.google.com/foo";
const char kChatAppURL[] = "https://talkgadget.google.com/hangouts/foo";
#endif
void AddContentTypeHandler(content::WebPluginInfo* info,
const char* mime_type,
const char* manifest_url) {
content::WebPluginMimeType mime_type_info;
mime_type_info.mime_type = mime_type;
mime_type_info.additional_params.emplace_back(
base::UTF8ToUTF16("nacl"), base::UTF8ToUTF16(manifest_url));
info->mime_types.push_back(mime_type_info);
}
} // namespace
class ChromeContentRendererClientTest : public testing::Test {
public:
void SetUp() override {
// Ensure that this looks like the renderer process based on the command
// line.
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kProcessType, switches::kRendererProcess);
}
};
#if BUILDFLAG(ENABLE_EXTENSIONS)
scoped_refptr<const extensions::Extension> CreateTestExtension(
extensions::Manifest::Location location, bool is_from_webstore,
bool is_hosted_app, const std::string& app_url) {
int flags = is_from_webstore ?
extensions::Extension::FROM_WEBSTORE:
extensions::Extension::NO_FLAGS;
base::DictionaryValue manifest;
manifest.SetString("name", "NaCl Extension");
manifest.SetString("version", "1");
manifest.SetInteger("manifest_version", 2);
if (is_hosted_app) {
auto url_list = std::make_unique<base::ListValue>();
url_list->AppendString(app_url);
manifest.Set(extensions::manifest_keys::kWebURLs, std::move(url_list));
manifest.SetString(extensions::manifest_keys::kLaunchWebURL, app_url);
}
std::string error;
return extensions::Extension::Create(base::FilePath(), location, manifest,
flags, &error);
}
scoped_refptr<const extensions::Extension> CreateExtension(
bool is_from_webstore) {
return CreateTestExtension(
extensions::Manifest::INTERNAL, is_from_webstore, kNotHostedApp,
std::string());
}
scoped_refptr<const extensions::Extension> CreateExtensionWithLocation(
extensions::Manifest::Location location, bool is_from_webstore) {
return CreateTestExtension(
location, is_from_webstore, kNotHostedApp, std::string());
}
scoped_refptr<const extensions::Extension> CreateHostedApp(
bool is_from_webstore, const std::string& app_url) {
return CreateTestExtension(extensions::Manifest::INTERNAL,
is_from_webstore,
kHostedApp,
app_url);
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(ChromeContentRendererClientTest, NaClRestriction) {
// Unknown content types have no NaCl module.
{
WebPluginInfo info;
EXPECT_EQ(GURL(),
ChromeContentRendererClient::GetNaClContentHandlerURL(
"application/x-foo", info));
}
// Known content types have a NaCl module.
{
WebPluginInfo info;
AddContentTypeHandler(&info, "application/x-foo", "www.foo.com");
EXPECT_EQ(GURL("www.foo.com"),
ChromeContentRendererClient::GetNaClContentHandlerURL(
"application/x-foo", info));
}
#if BUILDFLAG(ENABLE_NACL)
// --enable-nacl allows all NaCl apps.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(),
kNaClUnrestricted,
CreateExtension(kExtensionNotFromWebStore).get(),
&params));
}
// Unpacked extensions are allowed without --enable-nacl.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtensionWithLocation(extensions::Manifest::UNPACKED,
kExtensionNotFromWebStore).get(),
&params));
}
// Component extensions are allowed without --enable-nacl.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtensionWithLocation(extensions::Manifest::COMPONENT,
kExtensionNotFromWebStore).get(),
&params));
}
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_COMPONENT,
kExtensionNotFromWebStore).get(),
&params));
}
// Extensions that are force installed by policy are allowed without
// --enable-nacl.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtensionWithLocation(extensions::Manifest::EXTERNAL_POLICY,
kExtensionNotFromWebStore).get(),
&params));
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtensionWithLocation(
extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
kExtensionNotFromWebStore).get(),
&params));
}
// CWS extensions are allowed without --enable-nacl if called from an
// extension url.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL(kExtensionUrl),
kNaClRestricted,
CreateExtension(kExtensionFromWebStore).get(),
&params));
}
// Whitelisted URLs are allowed without --enable-nacl. There is a whitelist
// for the app URL and the manifest URL.
{
WebPluginParams params;
// Whitelisted Chat app is allowed.
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(kChatManifestFS),
GURL(kChatAppURL),
kNaClRestricted,
nullptr,
&params));
// Whitelisted manifest URL, bad app URLs, NOT allowed.
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(kChatManifestFS),
GURL("http://plus.google.com/foo"), // http scheme
kNaClRestricted, nullptr, &params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(kChatManifestFS),
GURL("http://plus.sandbox.google.com/foo"), // http scheme
kNaClRestricted, nullptr, &params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(kChatManifestFS),
GURL("https://plus.google.evil.com/foo"), // bad host
kNaClRestricted, nullptr, &params));
// Whitelisted app URL, bad manifest URL, NOT allowed.
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL("http://ssl.gstatic.com/s2/oz/nacl/foo"), // http scheme
GURL(kChatAppURL), kNaClRestricted, nullptr, &params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL("https://ssl.gstatic.evil.com/s2/oz/nacl/foo"), // bad host
GURL(kChatAppURL), kNaClRestricted, nullptr, &params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL("https://ssl.gstatic.com/wrong/s2/oz/nacl/foo"), // bad path
GURL(kChatAppURL), kNaClRestricted, nullptr, &params));
}
// Non-whitelisted URLs are blocked without --enable-nacl.
{
WebPluginParams params;
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL("https://plus.google.com.evil.com/foo1"),
kNaClRestricted,
nullptr,
&params));
}
// Non chrome-extension:// URLs belonging to hosted apps are allowed for
// webstore installed hosted apps.
{
WebPluginParams params;
EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL("http://example.com/test.html"),
kNaClRestricted,
CreateHostedApp(kExtensionFromWebStore,
"http://example.com/").get(),
&params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL("http://example.com/test.html"),
kNaClRestricted,
CreateHostedApp(kExtensionNotFromWebStore,
"http://example.com/").get(),
&params));
EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
GURL(),
GURL("http://example.evil.com/test.html"),
kNaClRestricted,
CreateHostedApp(kExtensionNotFromWebStore,
"http://example.com/").get(),
&params));
}
#endif // BUILDFLAG(ENABLE_NACL)
}
// SearchBouncer doesn't exist on Android.
#if !defined(OS_ANDROID)
TEST_F(ChromeContentRendererClientTest, ShouldSuppressErrorPage) {
ChromeContentRendererClient client;
SearchBouncer::GetInstance()->SetNewTabPageURL(GURL("http://example.com/n"));
EXPECT_FALSE(client.ShouldSuppressErrorPage(nullptr,
GURL("http://example.com")));
EXPECT_TRUE(client.ShouldSuppressErrorPage(nullptr,
GURL("http://example.com/n")));
SearchBouncer::GetInstance()->SetNewTabPageURL(GURL::EmptyGURL());
}
TEST_F(ChromeContentRendererClientTest, ShouldTrackUseCounter) {
ChromeContentRendererClient client;
SearchBouncer::GetInstance()->SetNewTabPageURL(GURL("http://example.com/n"));
EXPECT_TRUE(client.ShouldTrackUseCounter(GURL("http://example.com")));
EXPECT_FALSE(client.ShouldTrackUseCounter(GURL("http://example.com/n")));
SearchBouncer::GetInstance()->SetNewTabPageURL(GURL::EmptyGURL());
}
#endif
TEST_F(ChromeContentRendererClientTest, AddImageContextMenuPropertiesForLoFi) {
ChromeContentRendererClient client;
blink::WebURLResponse web_url_response;
web_url_response.AddHTTPHeaderField(
blink::WebString::FromUTF8(
data_reduction_proxy::chrome_proxy_content_transform_header()),
blink::WebString::FromUTF8(
data_reduction_proxy::empty_image_directive()));
std::map<std::string, std::string> properties;
client.AddImageContextMenuProperties(
web_url_response, /*is_image_in_context_a_placeholder_image=*/false,
&properties);
EXPECT_EQ(
data_reduction_proxy::empty_image_directive(),
properties
[data_reduction_proxy::chrome_proxy_content_transform_header()]);
}
TEST_F(ChromeContentRendererClientTest,
AddImageContextMenuPropertiesForPlaceholder) {
ChromeContentRendererClient client;
std::map<std::string, std::string> properties;
client.AddImageContextMenuProperties(
blink::WebURLResponse(), /*is_image_in_context_a_placeholder_image=*/true,
&properties);
EXPECT_EQ(
data_reduction_proxy::empty_image_directive(),
properties
[data_reduction_proxy::chrome_proxy_content_transform_header()]);
}