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 <jackmil@google.com>
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 <jackmil@google.com>
Reviewed-by: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
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