diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 88489da743f6d..ef2ec5e69b41c 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/ui/views/file_system_access/file_system_access_restore_permission_bubble_view_browsertest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view_browsertest.cc index 644b001715adf..b935d0553f311 100644 --- a/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view_browsertest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/file_system_access/file_system_access_restore_permission_bubble_view.h" #include "base/functional/callback_helpers.h" +#include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/profiles/profile.h" @@ -25,6 +26,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/page_transition_types.h" +#include "ui/views/widget/widget.h" using AccessType = FileSystemAccessPermissionRequestManager::Access; using HandleType = content::FileSystemAccessPermissionContext::HandleType; @@ -129,6 +131,195 @@ IN_PROC_BROWSER_TEST_F(FileSystemAccessRestorePermissionBubbleViewTest, EXPECT_EQ(callback_result, permissions::PermissionAction::DISMISSED); } +// =========================================================================== +// POC: Split-view highlight ring follows active_index_, not the FSA restore +// bubble's requesting WebContents. CWE-1007. +// +// Path (b): bubble is shown for the active pane (attacker.com). User then +// clicks the sibling pane (victim.com), which flips active_index_ inside the +// split. Because the FSA restore bubble has set_close_on_deactivate(false) and +// the requesting WebContents stays Visibility::VISIBLE during an intra-split +// switch, the bubble stays open. SplitTabHighlightController::OnActiveTabChange +// re-runs UpdateHighlight() with tracked_bubble_visibility_[FSA] still true, +// so the highlight ring migrates to the sibling pane while the open bubble +// still grants to attacker.com. +// =========================================================================== +IN_PROC_BROWSER_TEST_F(FileSystemAccessRestorePermissionBubbleViewTest, + POC_HighlightFollowsActiveIndexNotRequestingPane_PathB) { + // Tab 0 = attacker.com (will request FSA restore). + ASSERT_TRUE(AddTabAtIndex(0, GetURL("attacker.com"), + ui::PageTransition::PAGE_TRANSITION_TYPED)); + // Tab 1 = victim.com (sibling pane). + TabStripModel* const tab_strip_model = browser()->tab_strip_model(); + tab_strip_model->ActivateTabAt(1); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetURL("victim.com"))); + + // Put attacker.com (tab 0) and victim.com (tab 1) into a side-by-side split, + // with attacker.com active. + tab_strip_model->ActivateTabAt(0); + tab_strip_model->AddToNewSplit( + {1}, split_tabs::SplitTabVisualData(), + split_tabs::SplitTabCreatedSource::kToolbarButton); + ASSERT_EQ(tab_strip_model->active_index(), 0); + + std::vector contents_container_views = + BrowserView::GetBrowserViewForBrowser(browser()) + ->GetContentsContainerViews(); + ASSERT_EQ(contents_container_views.size(), 2U); + + content::WebContents* const attacker_wc = + tab_strip_model->GetWebContentsAt(0); + content::WebContents* const victim_wc = tab_strip_model->GetWebContentsAt(1); + ASSERT_EQ(attacker_wc->GetLastCommittedURL().host(), "attacker.com"); + ASSERT_EQ(victim_wc->GetLastCommittedURL().host(), "victim.com"); + + // Neither pane highlighted initially. + EXPECT_FALSE( + contents_container_views[0]->contents_outline_view()->is_highlighted()); + EXPECT_FALSE( + contents_container_views[1]->contents_outline_view()->is_highlighted()); + + // attacker.com (active pane 0) surfaces the FSA restore bubble. This is + // exactly what FileSystemAccessPermissionRequestManager::DequeueAndShowRequest + // does for kRestorePermissions — and CanShowRequest() has no active-tab gate. + const RequestData attacker_request( + RequestType::kRestorePermissions, + url::Origin::Create(attacker_wc->GetLastCommittedURL()), + {{content::PathInfo(FILE_PATH_LITERAL("/home/user/secret.txt")), + HandleType::kFile, AccessType::kReadWrite}}); + auto* bubble = GetFileSystemAccessRestorePermissionDialogForTesting( + attacker_request, base::DoNothing(), attacker_wc); + ASSERT_TRUE(bubble); + ASSERT_TRUE(bubble->GetWidget()); + ASSERT_TRUE(bubble->GetWidget()->IsVisible()); + // The bubble is bound to attacker.com's WebContents. + ASSERT_EQ(bubble->web_contents(), attacker_wc); + + // So far so good: highlight ring is on pane 0 (attacker.com), matching the + // bubble. + EXPECT_TRUE( + contents_container_views[0]->contents_outline_view()->is_highlighted()); + EXPECT_FALSE( + contents_container_views[1]->contents_outline_view()->is_highlighted()); + + // === User clicks the sibling pane (victim.com). === + // This is the intra-split active-tab change. Both WebContents stay + // Visibility::VISIBLE, so LocationBarBubbleDelegateView::OnVisibilityChanged + // does NOT close the bubble; set_close_on_deactivate(false) keeps it open. + tab_strip_model->ActivateTabAt(1); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(tab_strip_model->active_index(), 1); + + // The bubble for attacker.com is still showing. + ASSERT_TRUE(bubble->GetWidget()); + EXPECT_TRUE(bubble->GetWidget()->IsVisible()) + << "FSA restore bubble closed on intra-split switch (would mitigate)"; + EXPECT_EQ(bubble->web_contents(), attacker_wc) + << "Bubble is still bound to attacker.com"; + // attacker.com's WebContents is still VISIBLE (intra-split). + EXPECT_EQ(attacker_wc->GetVisibility(), content::Visibility::VISIBLE); + + // ========================== THE BUG ========================== + // SplitTabHighlightController::OnActiveTabChange -> UpdateHighlight() with + // tracked_bubble_visibility_[kFileSystemAccessBubbleElementIdentifier] still + // true -> SetHighlightActiveContentsView(true) -> highlight ring is now on + // pane 1 (victim.com), NOT on pane 0 which actually owns the open bubble. + // + // These EXPECTs PASS today, demonstrating the misattribution. A correct + // implementation would keep the highlight on pane 0 (or close the bubble). + EXPECT_TRUE( + contents_container_views[1]->contents_outline_view()->is_highlighted()) + << "BUG: highlight ring is on victim.com pane while FSA bubble is for " + "attacker.com"; + EXPECT_FALSE( + contents_container_views[0]->contents_outline_view()->is_highlighted()) + << "BUG: pane that actually owns the FSA bubble is NOT highlighted"; + + fprintf( + stderr, + "\n[POC] FSA restore bubble origin = %s (pane 0)\n" + "[POC] Highlighted pane = %d (host=%s)\n" + "[POC] => highlight ring misattributes the prompt to the sibling pane\n", + url::Origin::Create(attacker_wc->GetLastCommittedURL()) + .Serialize() + .c_str(), + contents_container_views[1]->contents_outline_view()->is_highlighted() + ? 1 + : 0, + std::string( + tab_strip_model->GetWebContentsAt(1)->GetLastCommittedURL().host()) + .c_str()); + + bubble->Close(); +} + +// Path (a): the *inactive* split pane surfaces the FSA restore bubble (no +// active-tab gate in CanShowRequest() / CreateAndShow()). The highlight ring is +// drawn around the *active* sibling pane. +IN_PROC_BROWSER_TEST_F(FileSystemAccessRestorePermissionBubbleViewTest, + POC_HighlightOnActivePaneWhenInactivePaneRequests) { + ASSERT_TRUE(AddTabAtIndex(0, GetURL("victim.com"), + ui::PageTransition::PAGE_TRANSITION_TYPED)); + TabStripModel* const tab_strip_model = browser()->tab_strip_model(); + tab_strip_model->ActivateTabAt(1); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetURL("attacker.com"))); + + // Split: pane 0 = victim.com (active), pane 1 = attacker.com (inactive). + tab_strip_model->ActivateTabAt(0); + tab_strip_model->AddToNewSplit( + {1}, split_tabs::SplitTabVisualData(), + split_tabs::SplitTabCreatedSource::kToolbarButton); + ASSERT_EQ(tab_strip_model->active_index(), 0); + + std::vector contents_container_views = + BrowserView::GetBrowserViewForBrowser(browser()) + ->GetContentsContainerViews(); + ASSERT_EQ(contents_container_views.size(), 2U); + + content::WebContents* const victim_wc = tab_strip_model->GetWebContentsAt(0); + content::WebContents* const attacker_wc = + tab_strip_model->GetWebContentsAt(1); + ASSERT_EQ(victim_wc->GetLastCommittedURL().host(), "victim.com"); + ASSERT_EQ(attacker_wc->GetLastCommittedURL().host(), "attacker.com"); + + // attacker.com is the INACTIVE pane but is allowed to surface the bubble: + // FileSystemAccessPermissionRequestManager::CanShowRequest() does not check + // tab_strip active state, unlike chooser_bubble_ui.cc. + const RequestData attacker_request( + RequestType::kRestorePermissions, + url::Origin::Create(attacker_wc->GetLastCommittedURL()), + {{content::PathInfo(FILE_PATH_LITERAL("/home/user/secret.txt")), + HandleType::kFile, AccessType::kReadWrite}}); + auto* bubble = GetFileSystemAccessRestorePermissionDialogForTesting( + attacker_request, base::DoNothing(), attacker_wc); + ASSERT_TRUE(bubble) << "Inactive pane was blocked from showing the bubble " + "(would mitigate path (a))"; + ASSERT_TRUE(bubble->GetWidget()->IsVisible()); + ASSERT_EQ(bubble->web_contents(), attacker_wc); + + // ========================== THE BUG ========================== + // ElementTracker fires Shown for kFileSystemAccessBubbleElementIdentifier in + // the window context -> SplitTabHighlightController::OnElementShown -> + // SetHighlightActiveContentsView(true) -> pane 0 (victim.com, the ACTIVE + // pane) is highlighted, even though the bubble belongs to pane 1 + // (attacker.com, the INACTIVE pane). + EXPECT_TRUE( + contents_container_views[0]->contents_outline_view()->is_highlighted()) + << "BUG: highlight ring on victim.com (active) while bubble is for " + "attacker.com (inactive pane)"; + EXPECT_FALSE( + contents_container_views[1]->contents_outline_view()->is_highlighted()) + << "BUG: requesting pane (attacker.com) is NOT highlighted"; + + fprintf(stderr, + "\n[POC] FSA bubble requested by inactive pane 1 (host=%s)\n" + "[POC] Highlight ring drawn on active pane 0 (host=%s)\n", + std::string(attacker_wc->GetLastCommittedURL().host()).c_str(), + std::string(victim_wc->GetLastCommittedURL().host()).c_str()); + + bubble->Close(); +} + IN_PROC_BROWSER_TEST_F(FileSystemAccessRestorePermissionBubbleViewTest, ShowFileSystemAccessDialog) { ASSERT_TRUE(AddTabAtIndex(0, GetURL("example.com"), diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal deleted file mode 160000 index d0158bfe38a17..0000000000000 --- a/third_party/search_engines_data/resources_internal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0158bfe38a1759ead13e53ed0332aefdb0f512b