blob: 6b05c9bf038755b4e1bef6f53a88dcd1857e8326 [file] [log] [blame]
// Copyright 2020 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 "base/json/json_reader.h"
#include "build/build_config.h"
#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
#include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
#include "components/subresource_filter/content/browser/ruleset_service.h"
#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
#include "components/subresource_filter/content/browser/test_ruleset_publisher.h"
#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "content/public/test/browser_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/subresource_filter_client_impl.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/grit/weblayer_resources.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
namespace {
// Returns whether a script resource that sets document.scriptExecuted to true
// on load was loaded.
bool WasParsedScriptElementLoaded(content::RenderFrameHost* rfh) {
DCHECK(rfh);
bool script_resource_was_loaded = false;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
rfh, "domAutomationController.send(!!document.scriptExecuted)",
&script_resource_was_loaded));
return script_resource_was_loaded;
}
} // namespace
class SubresourceFilterBrowserTest : public WebLayerBrowserTest {
public:
SubresourceFilterBrowserTest() = default;
~SubresourceFilterBrowserTest() override = default;
SubresourceFilterBrowserTest(const SubresourceFilterBrowserTest&) = delete;
SubresourceFilterBrowserTest& operator=(const SubresourceFilterBrowserTest&) =
delete;
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
}
protected:
void SetRulesetToDisallowURLsWithPathSuffix(const std::string& suffix) {
subresource_filter::testing::TestRulesetPair test_ruleset_pair;
subresource_filter::testing::TestRulesetCreator test_ruleset_creator;
test_ruleset_creator.CreateRulesetToDisallowURLsWithPathSuffix(
suffix, &test_ruleset_pair);
subresource_filter::testing::TestRulesetPublisher test_ruleset_publisher(
BrowserProcess::GetInstance()->subresource_filter_ruleset_service());
ASSERT_NO_FATAL_FAILURE(
test_ruleset_publisher.SetRuleset(test_ruleset_pair.unindexed));
}
};
// Tests that the ruleset service is available.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, RulesetService) {
EXPECT_NE(BrowserProcess::GetInstance()->subresource_filter_ruleset_service(),
nullptr);
}
// Tests that the ruleset is published as part of startup.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest, RulesArePublished) {
auto* ruleset_service =
BrowserProcess::GetInstance()->subresource_filter_ruleset_service();
// Publishing might or might not have already finished at this point; wait for
// it to finish if necessary.
if (!ruleset_service->GetMostRecentlyIndexedVersion().IsValid()) {
base::RunLoop run_loop;
ruleset_service->SetRulesetPublishedCallbackForTesting(
run_loop.QuitClosure());
run_loop.Run();
}
auto ruleset_version = ruleset_service->GetMostRecentlyIndexedVersion();
EXPECT_TRUE(ruleset_version.IsValid());
std::string most_recently_indexed_content_version =
ruleset_version.content_version;
std::string packaged_ruleset_manifest_string =
ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
IDR_SUBRESOURCE_FILTER_UNINDEXED_RULESET_MANIFEST_JSON);
auto packaged_ruleset_manifest =
base::JSONReader::Read(packaged_ruleset_manifest_string);
std::string* packaged_content_version =
packaged_ruleset_manifest->FindStringKey("version");
EXPECT_EQ(most_recently_indexed_content_version, *packaged_content_version);
}
// The below test is restricted to Android as it tests activation of the
// subresource filter in its default production configuration and WebLayer
// currently has a safe browsing database available in production only on
// Android; the safe browsing database being non-null is a prerequisite for
// subresource filter operation.
#if defined(OS_ANDROID)
// Tests that page activation state is computed as part of a pageload.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
PageActivationStateComputed) {
// Set up prereqs.
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
content::WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern(subresource_filter::kActivationConsoleMessage);
GURL test_url(embedded_test_server()->GetURL("/simple_page.html"));
subresource_filter::TestSubresourceFilterObserver observer(web_contents);
base::Optional<subresource_filter::mojom::ActivationLevel> page_activation =
observer.GetPageActivation(test_url);
EXPECT_FALSE(page_activation);
// Verify that a navigation results in both (a) the page activation level
// being computed, and (b) the result of that computation being the default
// level of "dry run" due to AdTagging.
NavigateAndWaitForCompletion(test_url, shell());
page_activation = observer.GetPageActivation(test_url);
EXPECT_TRUE(page_activation);
EXPECT_EQ(subresource_filter::mojom::ActivationLevel::kDryRun,
page_activation.value());
EXPECT_TRUE(console_observer.messages().empty());
}
#endif // (OS_ANDROID)
// Verifies that subframes that are flagged by the subresource filter ruleset
// are blocked from loading on activated URLs.
// Flaky on Windows. See https://crbug.com/1152429
#if defined(OS_WIN)
#define MAYBE_DisallowedSubframeURLBlockedOnActivatedURL \
DISABLED_DisallowedSubframeURLBlockedOnActivatedURL
#else
#define MAYBE_DisallowedSubframeURLBlockedOnActivatedURL \
DisallowedSubframeURLBlockedOnActivatedURL
#endif
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
MAYBE_DisallowedSubframeURLBlockedOnActivatedURL) {
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
content::WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern(subresource_filter::kActivationConsoleMessage);
GURL test_url(
embedded_test_server()->GetURL("/frame_with_included_script.html"));
subresource_filter::TestSubresourceFilterObserver observer(web_contents);
base::Optional<subresource_filter::mojom::ActivationLevel> page_activation =
observer.GetPageActivation(test_url);
EXPECT_FALSE(page_activation);
// Configure the database manager to activate on this URL.
scoped_refptr<FakeSafeBrowsingDatabaseManager> database_manager =
base::MakeRefCounted<FakeSafeBrowsingDatabaseManager>();
database_manager->AddBlocklistedUrl(
test_url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
auto* client_impl = static_cast<SubresourceFilterClientImpl*>(
subresource_filter::ContentSubresourceFilterThrottleManager::
FromWebContents(web_contents)
->client());
client_impl->set_database_manager_for_testing(database_manager);
// Verify that the "ad" subframe is loaded if it is not flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
"suffix-that-does-not-match-anything"));
NavigateAndWaitForCompletion(test_url, shell());
// The subresource filter should have been activated on this navigation...
page_activation = observer.GetPageActivation(test_url);
EXPECT_TRUE(page_activation);
EXPECT_EQ(subresource_filter::mojom::ActivationLevel::kEnabled,
page_activation.value());
EXPECT_FALSE(console_observer.messages().empty());
// ... but it should not have blocked the subframe from being loaded.
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Do a different-document navigation to ensure that that the next navigation
// to |test_url| executes as desired (e.g., to avoid any optimizations from
// being made due to it being a same-document navigation that would interfere
// with the logic of the test). Without this intervening navigation, we have
// seen flake on the Windows trybot that indicates that such optimizations are
// occurring.
NavigateAndWaitForCompletion(GURL("about:blank"), shell());
EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Verify that the "ad" subframe is blocked if it is flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(
SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Do a different-document navigation to ensure that that the next navigation
// to |test_url| executes as desired (e.g., to avoid any optimizations from
// being made due to it being a same-document navigation that would interfere
// with the logic of the test). Without this intervening navigation, we have
// seen flake on the Windows trybot that indicates that such optimizations are
// occurring.
NavigateAndWaitForCompletion(GURL("about:blank"), shell());
EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// The main frame document should never be filtered.
SetRulesetToDisallowURLsWithPathSuffix("frame_with_included_script.html");
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
}
// Verifies that subframes are not blocked on non-activated URLs.
IN_PROC_BROWSER_TEST_F(SubresourceFilterBrowserTest,
DisallowedSubframeURLNotBlockedOnNonActivatedURL) {
auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents();
GURL test_url(
embedded_test_server()->GetURL("/frame_with_included_script.html"));
// Verify that the "ad" subframe is loaded if it is not flagged by the
// ruleset.
ASSERT_NO_FATAL_FAILURE(SetRulesetToDisallowURLsWithPathSuffix(
"suffix-that-does-not-match-anything"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
// Verify that the "ad" subframe is loaded if even it is flagged by the
// ruleset as the URL is not activated.
ASSERT_NO_FATAL_FAILURE(
SetRulesetToDisallowURLsWithPathSuffix("included_script.js"));
NavigateAndWaitForCompletion(test_url, shell());
EXPECT_TRUE(WasParsedScriptElementLoaded(web_contents->GetMainFrame()));
}
} // namespace weblayer