blob: 5ef422a8916dbc452a5f168e7010d49eff94106e [file] [log] [blame]
// 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 <string>
#include <utility>
#include <vector>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/content_settings/tab_specific_content_settings.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_database_helper.h"
#include "chrome/browser/subresource_filter/chrome_subresource_filter_client.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/safe_browsing/db/util.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/subresource_filter/core/browser/subresource_filter_features.h"
#include "components/url_pattern_index/proto/rules.pb.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using safe_browsing::SubresourceFilterType;
using safe_browsing::SubresourceFilterLevel;
namespace subresource_filter {
namespace {
base::LazyInstance<std::vector<std::string>>::DestructorAtExit error_messages_ =
LAZY_INSTANCE_INITIALIZER;
class ScopedLoggingObserver {
public:
ScopedLoggingObserver() {
logging::SetLogMessageHandler(&ScopedLoggingObserver::LogHandler);
}
void RoundTripAndVerifyLogMessages(
content::WebContents* web_contents,
std::vector<std::string> messages_expected,
std::vector<std::string> messages_not_expected) {
// Round trip to the renderer to ensure the message would have gotten sent.
EXPECT_TRUE(content::ExecuteScript(web_contents, "var a = 1;"));
std::deque<bool> verify_messages_expected(messages_expected.size(), false);
for (size_t i = 0; i < messages_expected.size(); i++)
verify_messages_expected[i] = false;
for (const auto& message : error_messages_.Get()) {
for (size_t i = 0; i < messages_expected.size(); i++) {
verify_messages_expected[i] |=
(message.find(messages_expected[i]) != std::string::npos);
}
for (const auto& message_not_expected : messages_not_expected)
EXPECT_EQ(std::string::npos, message.find(message_not_expected))
<< message_not_expected;
}
for (size_t i = 0; i < messages_expected.size(); i++)
EXPECT_TRUE(verify_messages_expected[i]) << messages_expected[i];
}
private:
// Intercepts all console messages. Only used when the ConsoleObserverDelegate
// cannot be (e.g. when we need the standard delegate).
static bool LogHandler(int severity,
const char* file,
int line,
size_t message_start,
const std::string& str) {
if (file && std::string("CONSOLE") == file)
error_messages_.Get().push_back(str);
return false;
}
};
} // namespace
const char kSubresourceFilterActionsHistogram[] = "SubresourceFilter.Actions2";
// Tests that subresource_filter interacts well with the abusive enforcement in
// chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.
class SubresourceFilterPopupBrowserTest
: public SubresourceFilterListInsertingBrowserTest {
public:
void SetUpOnMainThread() override {
SubresourceFilterBrowserTest::SetUpOnMainThread();
Configuration config(subresource_filter::ActivationLevel::ENABLED,
subresource_filter::ActivationScope::ACTIVATION_LIST,
subresource_filter::ActivationList::BETTER_ADS);
ResetConfiguration(std::move(config));
ASSERT_NO_FATAL_FAILURE(
SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
}
void ConfigureAsAbusiveAndBetterAds(const GURL& url,
SubresourceFilterLevel abusive_level,
SubresourceFilterLevel bas_level) {
safe_browsing::ThreatMetadata metadata;
metadata.subresource_filter_match[SubresourceFilterType::ABUSIVE] =
abusive_level;
metadata.subresource_filter_match[SubresourceFilterType::BETTER_ADS] =
bas_level;
database_helper()->AddFullHashToDbAndFullHashCache(
url, safe_browsing::GetUrlSubresourceFilterId(), metadata);
}
bool AreDisallowedRequestsBlocked() {
std::string script = base::StringPrintf(
R"(
var script = document.createElement('script');
script.src = '%s';
script.type = 'text/javascript';
script.onload = () => { window.domAutomationController.send(true); }
script.onerror = () => { window.domAutomationController.send(false); }
document.head.appendChild(script);
)",
embedded_test_server()
->GetURL("/subresource_filter/included_script.js")
.spec()
.c_str());
bool loaded;
EXPECT_TRUE(
content::ExecuteScriptAndExtractBool(web_contents(), script, &loaded));
return !loaded;
}
};
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
NoConfiguration_AllowCreatingNewWindows) {
ResetConfiguration(Configuration::MakePresetForLiveRunOnPhishingSites());
base::HistogramTester tester;
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
// Only configure |a_url| as a phishing URL.
ConfigureAsPhishingURL(a_url);
// Navigate to a_url, should not trigger the popup blocker.
ui_test_utils::NavigateToURL(browser(), a_url);
bool opened_window = false;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&opened_window));
EXPECT_TRUE(opened_window);
EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
// Navigate again to trigger histogram logging. Make sure the navigation
// happens in the original WebContents.
browser()->tab_strip_model()->ToggleSelectionAt(
browser()->tab_strip_model()->GetIndexOfWebContents(web_contents));
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
BlockCreatingNewWindows) {
base::HistogramTester tester;
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
GURL b_url(embedded_test_server()->GetURL("b.com", kWindowOpenPath));
// Configure as abusive and BAS warn.
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::ENFORCE /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
// Navigate to a_url, should trigger the popup blocker.
ui_test_utils::NavigateToURL(browser(), a_url);
bool opened_window = false;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&opened_window));
EXPECT_FALSE(opened_window);
tester.ExpectTotalCount(kSubresourceFilterActionsHistogram, 0);
// Make sure the popup UI was shown.
EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
// Block again.
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&opened_window));
EXPECT_FALSE(opened_window);
// We are in warning mode for BAS, so no blocking.
EXPECT_FALSE(AreDisallowedRequestsBlocked());
// Navigate to |b_url|, which should successfully open the popup.
ui_test_utils::NavigateToURL(browser(), b_url);
opened_window = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&opened_window));
EXPECT_TRUE(opened_window);
// Popup UI should not be shown.
EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
BlockCreatingNewWindows_LogsToConsole) {
content::ConsoleObserverDelegate console_observer(web_contents(),
kAbusiveEnforceMessage);
web_contents()->SetDelegate(&console_observer);
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::ENFORCE /* abusive_level */,
SubresourceFilterLevel::ENFORCE /* bas_level */);
// Navigate to a_url, should trigger the popup blocker.
ui_test_utils::NavigateToURL(browser(), a_url);
bool opened_window = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "openWindow()", &opened_window));
EXPECT_FALSE(opened_window);
console_observer.Wait();
EXPECT_EQ(kAbusiveEnforceMessage, console_observer.message());
EXPECT_TRUE(AreDisallowedRequestsBlocked());
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
WarningDoNotBlockCreatingNewWindows_LogsToConsole) {
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::WARN /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
// Navigate to a_url, should log a warning and not trigger the popup blocker.
ScopedLoggingObserver log_observer;
ui_test_utils::NavigateToURL(browser(), a_url);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
log_observer.RoundTripAndVerifyLogMessages(
web_contents, {kActivationWarningConsoleMessage, kAbusiveWarnMessage},
{});
bool opened_window = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&opened_window));
EXPECT_TRUE(opened_window);
EXPECT_FALSE(AreDisallowedRequestsBlocked());
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
WarnAbusiveAndBetterAds_LogsToConsole) {
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::WARN /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
// Allow popups on |a_url|.
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(browser()->profile());
settings_map->SetContentSettingDefaultScope(
a_url, a_url, ContentSettingsType::CONTENT_SETTINGS_TYPE_POPUPS,
std::string(), CONTENT_SETTING_ALLOW);
// Navigate to a_url, should not trigger the popup blocker.
ScopedLoggingObserver log_observer;
ui_test_utils::NavigateToURL(browser(), a_url);
bool opened_window = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "openWindow()", &opened_window));
EXPECT_TRUE(opened_window);
EXPECT_FALSE(AreDisallowedRequestsBlocked());
log_observer.RoundTripAndVerifyLogMessages(
web_contents(), {kAbusiveWarnMessage, kActivationWarningConsoleMessage},
{});
}
// Whitelisted sites should not have console logging.
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
AllowCreatingNewWindows_NoLogToConsole) {
const char kWindowOpenPath[] = "/subresource_filter/window_open.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
// Enforce BAS, warn abusive.
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::WARN /* abusive_level */,
SubresourceFilterLevel::ENFORCE /* bas_level */);
// Allow popups on |a_url|.
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(browser()->profile());
settings_map->SetContentSettingDefaultScope(
a_url, a_url, ContentSettingsType::CONTENT_SETTINGS_TYPE_POPUPS,
std::string(), CONTENT_SETTING_ALLOW);
// Navigate to a_url, should not trigger the popup blocker.
ScopedLoggingObserver log_observer;
ui_test_utils::NavigateToURL(browser(), a_url);
EXPECT_TRUE(AreDisallowedRequestsBlocked());
bool opened_window = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents(), "openWindow()", &opened_window));
EXPECT_TRUE(opened_window);
// On the new window, requests should be allowed.
EXPECT_FALSE(AreDisallowedRequestsBlocked());
log_observer.RoundTripAndVerifyLogMessages(
web_contents(), {kActivationConsoleMessage}, {kAbusiveEnforceMessage});
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest, BlockOpenURLFromTab) {
base::HistogramTester tester;
const char kWindowOpenPath[] =
"/subresource_filter/window_open_spoof_click.html";
GURL a_url(embedded_test_server()->GetURL("a.com", kWindowOpenPath));
GURL b_url(embedded_test_server()->GetURL("b.com", kWindowOpenPath));
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::ENFORCE /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
// Navigate to a_url, should trigger the popup blocker.
ui_test_utils::NavigateToURL(browser(), a_url);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
tester.ExpectTotalCount(kSubresourceFilterActionsHistogram, 0);
EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
EXPECT_FALSE(AreDisallowedRequestsBlocked());
// Navigate to |b_url|, which should successfully open the popup.
ui_test_utils::NavigateToURL(browser(), b_url);
content::TestNavigationObserver navigation_observer(nullptr, 1);
navigation_observer.StartWatchingNewWebContents();
EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
navigation_observer.Wait();
// Popup UI should not be shown.
EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
EXPECT_FALSE(AreDisallowedRequestsBlocked());
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
BlockOpenURLFromTabInIframe) {
const char popup_path[] = "/subresource_filter/iframe_spoof_click_popup.html";
GURL a_url(embedded_test_server()->GetURL("a.com", popup_path));
ConfigureAsAbusiveAndBetterAds(
a_url, SubresourceFilterLevel::ENFORCE /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
// Navigate to a_url, should not trigger the popup blocker.
ui_test_utils::NavigateToURL(browser(), a_url);
bool sent_open = false;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
&sent_open));
EXPECT_TRUE(sent_open);
EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
EXPECT_FALSE(AreDisallowedRequestsBlocked());
}
IN_PROC_BROWSER_TEST_F(SubresourceFilterPopupBrowserTest,
TraditionalWindowOpen_NotBlocked) {
GURL url(GetTestUrl("/title2.html"));
ConfigureAsAbusiveAndBetterAds(
url, SubresourceFilterLevel::ENFORCE /* abusive_level */,
SubresourceFilterLevel::WARN /* bas_level */);
ui_test_utils::NavigateToURL(browser(), GetTestUrl("/title1.html"));
// Should not trigger the popup blocker because internally opens the tab with
// a user gesture.
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
->IsContentBlocked(CONTENT_SETTINGS_TYPE_POPUPS));
EXPECT_FALSE(AreDisallowedRequestsBlocked());
}
} // namespace subresource_filter