Chrome · File Input
CVE-2026-14031
Logic Error in File Input
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Pcontent/browser/web_contents_based_canceller_unittest.cc |
modified |
Files Changed
content/browser/web_contents_based_canceller.cccontent/browser/web_contents_based_canceller_unittest.cc
Patch
From 7a5f959341ce9650a847690ebbe10453b1f430f9 Mon Sep 17 00:00:00 2001 From: Jack Miller <[email protected]> Date: Fri, 29 May 2026 10:49:06 -0700 Subject: [PATCH] Handle occlusion in WebContentsBasedCanceller WebContentsBasedCanceller should consider views in both `Hidden` and `Occluded` state as not being visible. It previously considered only `Hidden` as not being visible. Bug: 495459838 Change-Id: Ie99c20bb01fa744c47eb05df99f4d8779114366f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7880412 Commit-Queue: Jack Miller <[email protected]> Reviewed-by: David Bienvenu <[email protected]> Reviewed-by: Arthur Sonzogni <[email protected]> Cr-Commit-Position: refs/heads/main@{#1638583} --- diff --git a/content/browser/web_contents_based_canceller.cc b/content/browser/web_contents_based_canceller.cc index 41db14d..51d9d865 100644 --- a/content/browser/web_contents_based_canceller.cc +++ b/content/browser/web_contents_based_canceller.cc @@ -47,7 +47,7 @@ bool WebContentsBasedCanceller::CanShowForVisibility(Visibility visibility) { return condition_ != CancelCondition::kVisibility || - visibility != Visibility::HIDDEN; + visibility == Visibility::VISIBLE; } bool WebContentsBasedCanceller::CanShowForRFHActiveState() { diff --git a/content/browser/web_contents_based_canceller_unittest.cc b/content/browser/web_contents_based_canceller_unittest.cc index 66a592dc..0f63755 100644 --- a/content/browser/web_contents_based_canceller_unittest.cc +++ b/content/browser/web_contents_based_canceller_unittest.cc @@ -66,6 +66,19 @@ } } +TEST_P(WebContentsBasedCancellerTest, CreateOccluded) { + web_contents()->WasOccluded(); + switch (GetParam()) { + case WebContentsBasedCanceller::CancelCondition::kActiveState: + EXPECT_NE(nullptr, CreateWebContentsBasedCanceller()); + break; + case WebContentsBasedCanceller::CancelCondition::kVisibility: + EXPECT_EQ(nullptr, CreateWebContentsBasedCanceller()) + << "Dialog was allowed for an OCCLUDED WebContents."; + break; + } +} + TEST_P(WebContentsBasedCancellerTest, BecomeInactive) { auto ac = CreateWebContentsBasedCanceller(); base::test::TestFuture<void> future; @@ -97,6 +110,32 @@ } } +TEST_P(WebContentsBasedCancellerTest, BecomeOccluded) { + auto ac = CreateWebContentsBasedCanceller(); + ASSERT_NE(nullptr, ac); + base::test::TestFuture<void> future; + ac->SetCancelCallback(future.GetCallback()); + web_contents()->WasOccluded(); + switch (GetParam()) { + case WebContentsBasedCanceller::CancelCondition::kActiveState: + EXPECT_FALSE(future.IsReady()); + break; + case WebContentsBasedCanceller::CancelCondition::kVisibility: +#if BUILDFLAG(IS_ANDROID) + // Android sends HIDDEN when picking a file. We should not cancel in this + // case. + // TODO(crbug.com/457495639): Figure out how to handle Android. + EXPECT_FALSE(future.IsReady()); +#else + EXPECT_TRUE(future.IsReady()) + << "Dialog was NOT cancelled when WebContents became OCCLUDED. " + << "The file picker remains visible over a foreign foreground " + << "window, enabling origin spoofing."; +#endif + break; + } +} + TEST_P(WebContentsBasedCancellerTest, InactiveBeforeSettingCallback) { auto ac = CreateWebContentsBasedCanceller(); main_rfh_impl()->SetLifecycleState( @@ -121,6 +160,22 @@ } } +TEST_P(WebContentsBasedCancellerTest, OccludedBeforeSettingCallback) { + auto ac = CreateWebContentsBasedCanceller(); + ASSERT_NE(nullptr, ac); + web_contents()->WasOccluded(); + base::test::TestFuture<void> future; + ac->SetCancelCallback(future.GetCallback()); + switch (GetParam()) { + case WebContentsBasedCanceller::CancelCondition::kActiveState: + EXPECT_FALSE(future.IsReady()); + break; + case WebContentsBasedCanceller::CancelCondition::kVisibility: + EXPECT_TRUE(future.IsReady()); + break; + } +} + // Tests that destroying does not call the callback. TEST_P(WebContentsBasedCancellerTest, Destroy) { auto ac = CreateWebContentsBasedCanceller();
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/web_contents_based_canceller_unittest.cc b/content/browser/web_contents_based_canceller_unittest.cc
index 66a592dc..0f63755 100644
--- a/content/browser/web_contents_based_canceller_unittest.cc
+++ b/content/browser/web_contents_based_canceller_unittest.cc
@@ -66,6 +66,19 @@
}
}
+TEST_P(WebContentsBasedCancellerTest, CreateOccluded) {
+ web_contents()->WasOccluded();
+ switch (GetParam()) {
+ case WebContentsBasedCanceller::CancelCondition::kActiveState:
+ EXPECT_NE(nullptr, CreateWebContentsBasedCanceller());
+ break;
+ case WebContentsBasedCanceller::CancelCondition::kVisibility:
+ EXPECT_EQ(nullptr, CreateWebContentsBasedCanceller())
+ << "Dialog was allowed for an OCCLUDED WebContents.";
+ break;
+ }
+}
+
TEST_P(WebContentsBasedCancellerTest, BecomeInactive) {
auto ac = CreateWebContentsBasedCanceller();
base::test::TestFuture<void> future;
@@ -97,6 +110,32 @@
}
}
+TEST_P(WebContentsBasedCancellerTest, BecomeOccluded) {
+ auto ac = CreateWebContentsBasedCanceller();
+ ASSERT_NE(nullptr, ac);
+ base::test::TestFuture<void> future;
+ ac->SetCancelCallback(future.GetCallback());
+ web_contents()->WasOccluded();
+ switch (GetParam()) {
+ case WebContentsBasedCanceller::CancelCondition::kActiveState:
+ EXPECT_FALSE(future.IsReady());
+ break;
+ case WebContentsBasedCanceller::CancelCondition::kVisibility:
+#if BUILDFLAG(IS_ANDROID)
+ // Android sends HIDDEN when picking a file. We should not cancel in this
+ // case.
+ // TODO(crbug.com/457495639): Figure out how to handle Android.
+ EXPECT_FALSE(future.IsReady());
+#else
+ EXPECT_TRUE(future.IsReady())
+ << "Dialog was NOT cancelled when WebContents became OCCLUDED. "
+ << "The file picker remains visible over a foreign foreground "
+ << "window, enabling origin spoofing.";
+#endif
+ break;
+ }
+}
+
TEST_P(WebContentsBasedCancellerTest, InactiveBeforeSettingCallback) {
auto ac = CreateWebContentsBasedCanceller();
main_rfh_impl()->SetLifecycleState(
@@ -121,6 +160,22 @@
}
}
+TEST_P(WebContentsBasedCancellerTest, OccludedBeforeSettingCallback) {
+ auto ac = CreateWebContentsBasedCanceller();
+ ASSERT_NE(nullptr, ac);
+ web_contents()->WasOccluded();
+ base::test::TestFuture<void> future;
+ ac->SetCancelCallback(future.GetCallback());
+ switch (GetParam()) {
+ case WebContentsBasedCanceller::CancelCondition::kActiveState:
+ EXPECT_FALSE(future.IsReady());
+ break;
+ case WebContentsBasedCanceller::CancelCondition::kVisibility:
+ EXPECT_TRUE(future.IsReady());
+ break;
+ }
+}
+
// Tests that destroying does not call the callback.
TEST_P(WebContentsBasedCancellerTest, Destroy) {
auto ac = CreateWebContentsBasedCanceller();
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page