CVE-2026-13842
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fios/web/web_state/ui/crw_web_controller_unittest.mm |
modified |
Files Changed
ios/web/web_state/ui/crw_web_controller_unittest.mmios/web/web_state/ui/crw_wk_ui_handler.mm
Patch
From b5e92a2937e988674e052b9060eed854256a5b5a Mon Sep 17 00:00:00 2001
From: Mike Dougherty <michaeldo@chromium.org>
Date: Mon, 15 Jun 2026 10:52:22 -0700
Subject: [PATCH] Prevent JS dialogs from non-visible WebStates
WebStates must not be permitted to display dialogs when they are no
longer the primary/visible WebState. Doing so would block the shared
WebKit WebProcess thread and impact other WebStates.
Fixed: 516836297
Change-Id: Ie470e00257a54292e8ae4c4fd8e236a01ed62368
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7934914
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1646934}
---
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 0823d10a..25dd689e2 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -466,6 +466,7 @@
JavaScriptDialogPresenterTest() : page_url_("https://chromium.test/") {}
void SetUp() override {
WebTestWithWebState::SetUp();
+ web_state()->WasShown();
LoadHtml(@"<html><body></body></html>", page_url_);
web_state()->SetDelegate(&web_state_delegate_);
}
@@ -605,6 +606,24 @@
ASSERT_TRUE(requested_prompt_dialogs().empty());
}
+// Tests that window.alert, window.confirm and window.prompt dialogs are not
+// shown if the WebState is not visible.
+TEST_F(JavaScriptDialogPresenterTest, InvisibleWebState) {
+ ASSERT_FALSE(JSDialogPresenterHasDialogs());
+
+ web_state()->WasHidden();
+ ASSERT_FALSE(web_state()->IsVisible());
+
+ ExecuteJavaScript(@"alert('test')");
+ ASSERT_TRUE(requested_alert_dialogs().empty());
+
+ EXPECT_NSEQ(@NO, ExecuteJavaScript(@"confirm('test')"));
+ ASSERT_TRUE(requested_confirm_dialogs().empty());
+
+ EXPECT_NSEQ([NSNull null], ExecuteJavaScript(@"prompt('Yes?', 'No')"));
+ ASSERT_TRUE(requested_prompt_dialogs().empty());
+}
+
// Test fixture for testing visible security state.
typedef WebTestWithWebState CRWWebStateSecurityStateTest;
diff --git a/ios/web/web_state/ui/crw_wk_ui_handler.mm b/ios/web/web_state/ui/crw_wk_ui_handler.mm
index 91d433b..728ac61e 100644
--- a/ios/web/web_state/ui/crw_wk_ui_handler.mm
+++ b/ios/web/web_state/ui/crw_wk_ui_handler.mm
@@ -366,6 +366,10 @@
return NO;
}
+ if (!self.webStateImpl->IsVisible()) {
+ return NO;
+ }
+
if (isMainFrame && url::Origin::Create(self.webStateImpl->GetVisibleURL()) !=
url::Origin::Create(requestURL)) {
// Dialog was requested by web page's main frame, but visible URL has
Regression Test / PoC
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 0823d10a..25dd689e2 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -466,6 +466,7 @@
JavaScriptDialogPresenterTest() : page_url_("https://chromium.test/") {}
void SetUp() override {
WebTestWithWebState::SetUp();
+ web_state()->WasShown();
LoadHtml(@"<html><body></body></html>", page_url_);
web_state()->SetDelegate(&web_state_delegate_);
}
@@ -605,6 +606,24 @@
ASSERT_TRUE(requested_prompt_dialogs().empty());
}
+// Tests that window.alert, window.confirm and window.prompt dialogs are not
+// shown if the WebState is not visible.
+TEST_F(JavaScriptDialogPresenterTest, InvisibleWebState) {
+ ASSERT_FALSE(JSDialogPresenterHasDialogs());
+
+ web_state()->WasHidden();
+ ASSERT_FALSE(web_state()->IsVisible());
+
+ ExecuteJavaScript(@"alert('test')");
+ ASSERT_TRUE(requested_alert_dialogs().empty());
+
+ EXPECT_NSEQ(@NO, ExecuteJavaScript(@"confirm('test')"));
+ ASSERT_TRUE(requested_confirm_dialogs().empty());
+
+ EXPECT_NSEQ([NSNull null], ExecuteJavaScript(@"prompt('Yes?', 'No')"));
+ ASSERT_TRUE(requested_prompt_dialogs().empty());
+}
+
// Test fixture for testing visible security state.
typedef WebTestWithWebState CRWWebStateSecurityStateTest;
Original Bug Report
Chrome iOS: Persistent Address Bar Origin Spoofing via JavaScript Thread Blocking
Steps to reproduce the problem
- Open: https://justluckidzz.github.io/clickme/zero.html
- Tap CLICK HERE
- Observe: Address bar changes to apple.com, attacker controlled content remains rendered, redirect becomes stuck/persistent & swipe refresh and browser UI interactions still keep apple.com visible while attacker content remains displayed
Problem Description
Broken Behavior:
Chrome iOS contains a persistent address bar spoofing vulnerability caused by a UI/render desynchronization during asynchronous navigation. The issue occurs when attacker-controlled HTML is injected into a newly opened about:blank tab using document.write(), followed by asynchronous navigation to a trusted origin (https://apple.com) while a synchronous modal dialog (prompt()) blocks the navigation/render pipeline.
Trigger logic:
setTimeout(() => {
newTab.location.href = "https://apple.com";
}, 1);
setTimeout(function () {
prompt("ROOT CAUSE");
}, 500);
Chrome iOS updates the address bar and security UI to display apple.com before navigation fully commits. The renderer then becomes stuck displaying attacker-controlled HTML content while the browser UI continues displaying the trusted origin.
The spoofed state remains persistent even after:
- Swipe refresh
- Browser UI interaction
- Navigation gestures
Expected Behavior:
The address bar must strictly synchronize with the currently rendered DOM. If the main thread is blocked before a navigation commits, the address bar should reflect the true origin of the visible content, or the navigation should complete without freezing the UI state.
Reproduced on:
- Chrome iOS Version: 149.0.7827.26 (Stable) | Also confirmed on Beta, Dev, and Canary latest version.
- iOS 26.6 Beta Developer
Summary
Chrome iOS: Persistent Address Bar Origin Spoofing via JavaScript Thread Blocking
Custom Questions
Type of crash:
na
Crash state:
na
Reporter credit:
Azza Tegar Naufal Ataullah
Additional Data
Category: Security
Chrome Channel: Stable
Regression: N/A \