1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h"
6
7 #include <memory>
8 #include <sstream>
9 #include <string>
10
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
18 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
19 #include "components/subresource_filter/content/browser/subframe_navigation_test_utils.h"
20 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
21 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
22 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
23 #include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
24 #include "content/public/browser/navigation_handle.h"
25 #include "content/public/browser/web_contents_observer.h"
26 #include "content/public/test/navigation_simulator.h"
27 #include "content/public/test/test_renderer_host.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "url/gurl.h"
30 #include "url/origin.h"
31
32 namespace subresource_filter {
33
34 class MockDelegate : public SubframeNavigationFilteringThrottle::Delegate {
35 public:
36 MockDelegate() = default;
37 ~MockDelegate() override = default;
38
39 // SubframeNavigationFilteringThrottle::Delegate:
CalculateIsAdSubframe(content::RenderFrameHost * frame_host,LoadPolicy load_policy)40 bool CalculateIsAdSubframe(content::RenderFrameHost* frame_host,
41 LoadPolicy load_policy) override {
42 return false;
43 }
44
45 DISALLOW_COPY_AND_ASSIGN(MockDelegate);
46 };
47
48 class SubframeNavigationFilteringThrottleTest
49 : public content::RenderViewHostTestHarness,
50 public content::WebContentsObserver {
51 public:
SubframeNavigationFilteringThrottleTest()52 SubframeNavigationFilteringThrottleTest() {}
~SubframeNavigationFilteringThrottleTest()53 ~SubframeNavigationFilteringThrottleTest() override {}
54
SetUp()55 void SetUp() override {
56 content::RenderViewHostTestHarness::SetUp();
57 NavigateAndCommit(GURL("https://example.test"));
58 Observe(RenderViewHostTestHarness::web_contents());
59 }
60
TearDown()61 void TearDown() override {
62 dealer_handle_.reset();
63 ruleset_handle_.reset();
64 parent_filter_.reset();
65 RunUntilIdle();
66 content::RenderViewHostTestHarness::TearDown();
67 }
68
navigation_simulator()69 content::NavigationSimulator* navigation_simulator() {
70 return navigation_simulator_.get();
71 }
72
73 // content::WebContentsObserver:
DidStartNavigation(content::NavigationHandle * navigation_handle)74 void DidStartNavigation(
75 content::NavigationHandle* navigation_handle) override {
76 ASSERT_FALSE(navigation_handle->IsInMainFrame());
77 // The |parent_filter_| is the parent frame's filter. Do not register a
78 // throttle if the parent is not activated with a valid filter.
79 if (parent_filter_) {
80 auto throttle = std::make_unique<SubframeNavigationFilteringThrottle>(
81 navigation_handle, parent_filter_.get(), &mock_delegate_);
82 ASSERT_NE(nullptr, throttle->GetNameForLogging());
83 navigation_handle->RegisterThrottleForTesting(std::move(throttle));
84 }
85 }
86
InitializeDocumentSubresourceFilter(const GURL & document_url,mojom::ActivationLevel parent_level=mojom::ActivationLevel::kEnabled)87 void InitializeDocumentSubresourceFilter(
88 const GURL& document_url,
89 mojom::ActivationLevel parent_level = mojom::ActivationLevel::kEnabled) {
90 ASSERT_NO_FATAL_FAILURE(
91 test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
92 "disallowed.html", &test_ruleset_pair_));
93
94 // Make the blocking task runner run on the current task runner for the
95 // tests, to ensure that the NavigationSimulator properly runs all necessary
96 // tasks while waiting for throttle checks to finish.
97 dealer_handle_ = std::make_unique<VerifiedRulesetDealer::Handle>(
98 base::ThreadTaskRunnerHandle::Get());
99 dealer_handle_->TryOpenAndSetRulesetFile(test_ruleset_pair_.indexed.path,
100 /*expected_checksum=*/0,
101 base::DoNothing());
102 ruleset_handle_ =
103 std::make_unique<VerifiedRuleset::Handle>(dealer_handle_.get());
104
105 testing::TestActivationStateCallbackReceiver activation_state;
106 mojom::ActivationState parent_activation_state;
107 parent_activation_state.activation_level = parent_level;
108 parent_activation_state.enable_logging = true;
109 parent_filter_ = std::make_unique<AsyncDocumentSubresourceFilter>(
110 ruleset_handle_.get(),
111 AsyncDocumentSubresourceFilter::InitializationParams(
112 document_url, url::Origin::Create(document_url),
113 parent_activation_state),
114 activation_state.GetCallback());
115 RunUntilIdle();
116 activation_state.ExpectReceivedOnce(parent_activation_state);
117 }
118
RunUntilIdle()119 void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
120
CreateTestSubframeAndInitNavigation(const GURL & first_url,content::RenderFrameHost * parent)121 void CreateTestSubframeAndInitNavigation(const GURL& first_url,
122 content::RenderFrameHost* parent) {
123 content::RenderFrameHost* render_frame =
124 content::RenderFrameHostTester::For(parent)->AppendChild(
125 base::StringPrintf("subframe-%s", first_url.spec().c_str()));
126 navigation_simulator_ =
127 content::NavigationSimulator::CreateRendererInitiated(first_url,
128 render_frame);
129 }
130
GetConsoleMessages()131 const std::vector<std::string>& GetConsoleMessages() {
132 return content::RenderFrameHostTester::For(main_rfh())
133 ->GetConsoleMessages();
134 }
135
GetFilterConsoleMessage(const GURL & filtered_url)136 std::string GetFilterConsoleMessage(const GURL& filtered_url) {
137 return base::StringPrintf(kDisallowSubframeConsoleMessageFormat,
138 filtered_url.possibly_invalid_spec().c_str());
139 }
140
141 private:
142 testing::TestRulesetCreator test_ruleset_creator_;
143 testing::TestRulesetPair test_ruleset_pair_;
144
145 MockDelegate mock_delegate_;
146 std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle_;
147 std::unique_ptr<VerifiedRuleset::Handle> ruleset_handle_;
148
149 std::unique_ptr<AsyncDocumentSubresourceFilter> parent_filter_;
150
151 std::unique_ptr<content::NavigationSimulator> navigation_simulator_;
152
153 DISALLOW_COPY_AND_ASSIGN(SubframeNavigationFilteringThrottleTest);
154 };
155
TEST_F(SubframeNavigationFilteringThrottleTest,FilterOnStart)156 TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnStart) {
157 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
158 const GURL url("https://example.test/disallowed.html");
159 CreateTestSubframeAndInitNavigation(url, main_rfh());
160 EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
161 SimulateStartAndGetResult(navigation_simulator()));
162 EXPECT_TRUE(
163 base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
164 }
165
TEST_F(SubframeNavigationFilteringThrottleTest,FilterOnRedirect)166 TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnRedirect) {
167 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
168 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
169 main_rfh());
170
171 EXPECT_EQ(content::NavigationThrottle::PROCEED,
172 SimulateStartAndGetResult(navigation_simulator()));
173 EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
174 SimulateRedirectAndGetResult(
175 navigation_simulator(),
176 GURL("https://example.test/disallowed.html")));
177 }
178
TEST_F(SubframeNavigationFilteringThrottleTest,DryRunOnStart)179 TEST_F(SubframeNavigationFilteringThrottleTest, DryRunOnStart) {
180 InitializeDocumentSubresourceFilter(GURL("https://example.test"),
181 mojom::ActivationLevel::kDryRun);
182 const GURL url("https://example.test/disallowed.html");
183 CreateTestSubframeAndInitNavigation(url, main_rfh());
184
185 EXPECT_EQ(content::NavigationThrottle::PROCEED,
186 SimulateStartAndGetResult(navigation_simulator()));
187 EXPECT_FALSE(
188 base::Contains(GetConsoleMessages(), GetFilterConsoleMessage(url)));
189 }
190
TEST_F(SubframeNavigationFilteringThrottleTest,DryRunOnRedirect)191 TEST_F(SubframeNavigationFilteringThrottleTest, DryRunOnRedirect) {
192 InitializeDocumentSubresourceFilter(GURL("https://example.test"),
193 mojom::ActivationLevel::kDryRun);
194 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
195 main_rfh());
196
197 EXPECT_EQ(content::NavigationThrottle::PROCEED,
198 SimulateStartAndGetResult(navigation_simulator()));
199 EXPECT_EQ(content::NavigationThrottle::PROCEED,
200 SimulateRedirectAndGetResult(
201 navigation_simulator(),
202 GURL("https://example.test/disallowed.html")));
203 }
204
TEST_F(SubframeNavigationFilteringThrottleTest,FilterOnSecondRedirect)205 TEST_F(SubframeNavigationFilteringThrottleTest, FilterOnSecondRedirect) {
206 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
207 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
208 main_rfh());
209
210 EXPECT_EQ(content::NavigationThrottle::PROCEED,
211 SimulateStartAndGetResult(navigation_simulator()));
212 EXPECT_EQ(
213 content::NavigationThrottle::PROCEED,
214 SimulateRedirectAndGetResult(navigation_simulator(),
215 GURL("https://example.test/allowed2.html")));
216 EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
217 SimulateRedirectAndGetResult(
218 navigation_simulator(),
219 GURL("https://example.test/disallowed.html")));
220 }
221
TEST_F(SubframeNavigationFilteringThrottleTest,NeverFilterNonMatchingRule)222 TEST_F(SubframeNavigationFilteringThrottleTest, NeverFilterNonMatchingRule) {
223 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
224 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
225 main_rfh());
226
227 EXPECT_EQ(content::NavigationThrottle::PROCEED,
228 SimulateStartAndGetResult(navigation_simulator()));
229 EXPECT_EQ(
230 content::NavigationThrottle::PROCEED,
231 SimulateRedirectAndGetResult(navigation_simulator(),
232 GURL("https://example.test/allowed2.html")));
233 EXPECT_EQ(content::NavigationThrottle::PROCEED,
234 SimulateCommitAndGetResult(navigation_simulator()));
235 }
236
TEST_F(SubframeNavigationFilteringThrottleTest,FilterSubsubframe)237 TEST_F(SubframeNavigationFilteringThrottleTest, FilterSubsubframe) {
238 // Fake an activation of the subframe.
239 content::RenderFrameHost* parent_subframe =
240 content::RenderFrameHostTester::For(main_rfh())
241 ->AppendChild("parent-sub");
242 GURL test_url = GURL("https://example.test");
243 auto navigation = content::NavigationSimulator::CreateRendererInitiated(
244 test_url, parent_subframe);
245 navigation->Start();
246 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
247 navigation->Commit();
248
249 CreateTestSubframeAndInitNavigation(
250 GURL("https://example.test/disallowed.html"), parent_subframe);
251 EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
252 SimulateStartAndGetResult(navigation_simulator()));
253 }
254
TEST_F(SubframeNavigationFilteringThrottleTest,DelayMetrics)255 TEST_F(SubframeNavigationFilteringThrottleTest, DelayMetrics) {
256 base::HistogramTester histogram_tester;
257 InitializeDocumentSubresourceFilter(GURL("https://example.test"));
258 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
259 main_rfh());
260 navigation_simulator()->SetTransition(ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
261 EXPECT_EQ(content::NavigationThrottle::PROCEED,
262 SimulateStartAndGetResult(navigation_simulator()));
263 EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
264 SimulateRedirectAndGetResult(
265 navigation_simulator(),
266 GURL("https://example.test/disallowed.html")));
267
268 navigation_simulator()->CommitErrorPage();
269
270 const char kFilterDelayDisallowed[] =
271 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed2";
272 const char kFilterDelayWouldDisallow[] =
273 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.WouldDisallow";
274 const char kFilterDelayAllowed[] =
275 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed";
276 histogram_tester.ExpectTotalCount(kFilterDelayDisallowed, 1);
277 histogram_tester.ExpectTotalCount(kFilterDelayWouldDisallow, 0);
278 histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 0);
279
280 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
281 main_rfh());
282 EXPECT_EQ(content::NavigationThrottle::PROCEED,
283 SimulateStartAndGetResult(navigation_simulator()));
284 EXPECT_EQ(content::NavigationThrottle::PROCEED,
285 SimulateCommitAndGetResult(navigation_simulator()));
286
287 histogram_tester.ExpectTotalCount(kFilterDelayDisallowed, 1);
288 histogram_tester.ExpectTotalCount(kFilterDelayWouldDisallow, 0);
289 histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 1);
290 }
291
TEST_F(SubframeNavigationFilteringThrottleTest,DelayMetricsDryRun)292 TEST_F(SubframeNavigationFilteringThrottleTest, DelayMetricsDryRun) {
293 base::HistogramTester histogram_tester;
294 InitializeDocumentSubresourceFilter(GURL("https://example.test"),
295 mojom::ActivationLevel::kDryRun);
296 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
297 main_rfh());
298 navigation_simulator()->SetTransition(ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
299 EXPECT_EQ(content::NavigationThrottle::PROCEED,
300 SimulateStartAndGetResult(navigation_simulator()));
301 EXPECT_EQ(content::NavigationThrottle::PROCEED,
302 SimulateRedirectAndGetResult(
303 navigation_simulator(),
304 GURL("https://example.test/disallowed.html")));
305 navigation_simulator()->Commit();
306
307 const char kFilterDelayDisallowed[] =
308 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed2";
309 const char kFilterDelayWouldDisallow[] =
310 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.WouldDisallow";
311 const char kFilterDelayAllowed[] =
312 "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed";
313 histogram_tester.ExpectTotalCount(kFilterDelayDisallowed, 0);
314 histogram_tester.ExpectTotalCount(kFilterDelayWouldDisallow, 1);
315 histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 0);
316
317 CreateTestSubframeAndInitNavigation(GURL("https://example.test/allowed.html"),
318 main_rfh());
319 EXPECT_EQ(content::NavigationThrottle::PROCEED,
320 SimulateStartAndGetResult(navigation_simulator()));
321 EXPECT_EQ(content::NavigationThrottle::PROCEED,
322 SimulateCommitAndGetResult(navigation_simulator()));
323
324 histogram_tester.ExpectTotalCount(kFilterDelayDisallowed, 0);
325 histogram_tester.ExpectTotalCount(kFilterDelayWouldDisallow, 1);
326 histogram_tester.ExpectTotalCount(kFilterDelayAllowed, 1);
327 }
328
329 } // namespace subresource_filter
330