diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 88489da743f6dd..ef2ec5e69b41c3 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py @@ -669,4 +669,5 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + #sys.exit(main()) + pass diff --git a/chrome/browser/subresource_filter/subresource_filter_settings_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_settings_browsertest.cc index f23b7093246208..9f9dbb23378198 100644 --- a/chrome/browser/subresource_filter/subresource_filter_settings_browsertest.cc +++ b/chrome/browser/subresource_filter/subresource_filter_settings_browsertest.cc @@ -12,6 +12,10 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" +#include "chrome/browser/ui/views/content_setting_bubble_contents.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.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" @@ -32,6 +36,8 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" +#include "ui/views/test/widget_test.h" #include "url/gurl.h" namespace subresource_filter { @@ -312,4 +318,87 @@ IN_PROC_BROWSER_TEST_F(SubresourceFilterSettingsBrowserTest, subresource_filter::SubresourceFilterAction::kUISuppressed, 1); } +// Regression / proof-of-concept test: +// ContentSettingSubresourceFilterBubbleModel::CommitChanges() live-reads +// web_contents()->GetPrimaryPage() at WindowClosing() time. Because +// ContentSettingBubbleContents::PrimaryPageChanged() closes the widget +// asynchronously, a renderer-initiated cross-origin navigation that commits +// while the bubble is open (with the "Always allow ads" box checked) causes the +// persistent ADS allowlist entry to be written for the *destination* origin +// instead of the origin the bubble was opened for. +IN_PROC_BROWSER_TEST_F(SubresourceFilterSettingsBrowserTest, + AdsBubbleCommitAllowlistsPostNavOrigin) { + ASSERT_NO_FATAL_FAILURE( + SetRulesetToDisallowURLsWithPathSuffix("included_script.js")); + + GURL a_url(embedded_test_server()->GetURL( + "a.com", "/subresource_filter/frame_with_included_script.html")); + GURL b_url(embedded_test_server()->GetURL( + "b.com", "/subresource_filter/frame_with_included_script.html")); + ConfigureAsPhishingURL(a_url); + + // Step 1: User visits attacker-controlled origin A; the subresource filter + // activates and the omnibox "Ads blocked" indicator appears. + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url)); + ASSERT_FALSE( + WasParsedScriptElementLoaded(web_contents()->GetPrimaryMainFrame())); + + // Precondition: neither origin is allowlisted yet. + ASSERT_NE(CONTENT_SETTING_ALLOW, settings_manager()->GetSitePermission(a_url)); + ASSERT_NE(CONTENT_SETTING_ALLOW, settings_manager()->GetSitePermission(b_url)); + + // Step 2: User clicks the indicator -> the SubresourceFilter bubble opens. + // We construct exactly the production view+model pair that + // ContentSettingImageView::ShowBubbleImpl() would create, anchored to the + // location bar. + auto bubble_model = + std::make_unique( + /*delegate=*/nullptr, web_contents()); + ContentSettingBubbleModel* model = bubble_model.get(); + views::View* anchor = + BrowserView::GetBrowserViewForBrowser(browser())->GetLocationBarView(); + auto* bubble_contents = new ContentSettingBubbleContents( + std::move(bubble_model), web_contents(), views::BubbleAnchor(anchor), + views::BubbleBorder::TOP_RIGHT); + views::Widget* bubble_widget = + views::BubbleDialogDelegateView::CreateBubble(bubble_contents); + bubble_widget->Show(); + + // Step 3: User checks the "Always allow ads on this site" checkbox. + model->OnManageCheckboxChecked(true); + + views::test::WidgetDestroyedWaiter destroyed_waiter(bubble_widget); + + // Step 4: Before the user clicks Done, attacker JS on origin A performs a + // renderer-initiated cross-origin navigation to origin B. + { + content::TestNavigationObserver nav_observer(web_contents(), 1); + ASSERT_TRUE(content::ExecJs( + web_contents()->GetPrimaryMainFrame(), + "location.href = '" + b_url.spec() + "';", + content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); + nav_observer.Wait(); + } + ASSERT_EQ(b_url, web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL()); + + // Step 5: PrimaryPageChanged() fired during commit and posted an async + // Widget::Close. When that task runs, WindowClosing() -> + // CommitChanges() -> live-read GetPrimaryPage() (now origin B) -> + // OnReloadRequested() -> AllowlistSite(B) + Reload(). + destroyed_waiter.Wait(); + // OnReloadRequested() also issued a Reload(); let it settle. + ASSERT_TRUE(content::WaitForLoadStop(web_contents())); + + // Step 6: BUG -- origin B holds the persistent ADS=ALLOW entry; origin A, + // where the user actually opened the bubble, does not. + EXPECT_NE(CONTENT_SETTING_ALLOW, + settings_manager()->GetSitePermission(a_url)); + EXPECT_EQ(CONTENT_SETTING_ALLOW, + settings_manager()->GetSitePermission(b_url)); + LOG(ERROR) << "[POC] ADS permission for a.com (bubble opened here): " + << settings_manager()->GetSitePermission(a_url); + LOG(ERROR) << "[POC] ADS permission for b.com (navigated here): " + << settings_manager()->GetSitePermission(b_url); +} + } // namespace subresource_filter diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal deleted file mode 160000 index d0158bfe38a175..00000000000000 --- a/third_party/search_engines_data/resources_internal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0158bfe38a1759ead13e53ed0332aefdb0f512b