CVE-2026-113224
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java |
modified |
Files Changed
chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.javachrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.javachrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java
Patch
From eccd6ead90856b24c15718976c1dc167ec148b52 Mon Sep 17 00:00:00 2001 From: Yaron Friedman <[email protected]> Date: Tue, 15 Sep 2026 07:11:58 -0700 Subject: [PATCH] Enable touch filtering and tap protection in WebAPK update dialogs Configure touch filtering and an initial button tap protection delay when constructing the modal dialog models for the WebAPK icon and name update prompt and for the uninstall confirmation prompt it can lead to. This ensures obscured touch events are filtered out and prevents unintentional immediate button activations when the dialogs are presented to the user. The original patch for WebApkIconNameUpdateDialog was generated by CodeMender. This CL differs from that patch by logically carrying the fix forward to the destructive action. WebApkUpdateReportAbuseDialog is reached from the update prompt's negative button, and its positive button leads to app uninstallation. Protecting only the first prompt would leave the tapjacking target that actually destroys user state unguarded, so the uninstall confirmation is given the same treatment, with matching test coverage. This mirrors the protection already applied to comparable prompts, including AddToHomescreenDialogView and ExtensionInstallDialogBridge. Bug: 518108422 Co-Authored-By: Yoshisato Yanagisawa <[email protected]> Change-Id: Icd12dcdb71b3daa41f1feffa2ed269989a1bc61b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8394200 Reviewed-by: Glenn Hartmann <[email protected]> Commit-Queue: Yaron Friedman <[email protected]> Cr-Commit-Position: refs/heads/main@{#1698059} --- diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java index 604c6aa..1634b57 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java @@ -16,6 +16,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.ui.LayoutInflaterUtils; +import org.chromium.ui.UiUtils; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogProperties; @@ -155,6 +156,10 @@ resources, R.string.webapp_update_negative_button) .with(ModalDialogProperties.TITLE_SCROLLABLE, true) + .with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true) + .with( + ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS, + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS) .build(); mModalDialogManager = manager; diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java index e79a6b0..31644764 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateReportAbuseDialog.java @@ -19,6 +19,7 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.ui.LayoutInflaterUtils; +import org.chromium.ui.UiUtils; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogProperties; @@ -95,7 +96,11 @@ .with( ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, - R.string.webapk_report_abuse_cancel); + R.string.webapk_report_abuse_cancel) + .with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true) + .with( + ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS, + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS); if (mShowAbuseCheckbox) { View dialogCustomView = LayoutInflaterUtils.inflate( diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java index 36b625a8..df6cf47 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialogTest.java @@ -27,6 +27,7 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; +import org.chromium.ui.UiUtils; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogProperties; @@ -219,6 +220,13 @@ : null, getUpdateDialogBitmap(R.id.app_icon_new)); + PropertyModel dialogModel = mDialogManager.getCurrentDialogModel(); + Assert.assertNotNull(dialogModel); + Assert.assertTrue(dialogModel.get(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY)); + Assert.assertEquals( + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS, + (long) dialogModel.get(ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS)); + mDialogManager.dismissCurrentDialog( clickAccept ? DialogDismissalCause.POSITIVE_BUTTON_CLICKED @@ -258,6 +266,13 @@ Assert.assertEquals(expectedTitle, getDialogTitle()); + PropertyModel abuseDialogModel = mDialogManager.getCurrentDialogModel(); + Assert.assertNotNull(abuseDialogModel); + Assert.assertTrue(abuseDialogModel.get(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY)); + Assert.assertEquals( + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS, + (long) abuseDialogModel.get(ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS)); + mDialogManager.dismissCurrentDialog( clickAccept ? DialogDismissalCause.POSITIVE_BUTTON_CLICKED @@ -375,4 +390,44 @@ // Make sure Canceling the dialog does the right thing. verifyReportAbuseValues(/* clickAccept= */ false, "short", "Uninstall 'short'?"); } + + @Test + @SmallTest + @Feature({"Webapps"}) + public void testDialogInputProtection() { + DialogParams dialogParams = DialogParams.createDefault(); + dialogParams.iconChanged = true; + dialogParams.bitmapBefore = generateTestBitmap(Color.BLUE); + dialogParams.bitmapAfter = generateTestBitmap(Color.RED); + + WebApkIconNameUpdateDialog dialog = new WebApkIconNameUpdateDialog(); + Context context = + new ContextThemeWrapper( + ApplicationProvider.getApplicationContext(), + R.style.Theme_BrowserUI_DayNight); + + dialog.show( + context, + mDialogManager, + /* packageName= */ "", + dialogParams.iconChanged, + dialogParams.shortNameChanged, + dialogParams.nameChanged, + dialogParams.shortNameBefore, + dialogParams.shortNameAfter, + dialogParams.nameBefore, + dialogParams.nameAfter, + dialogParams.bitmapBefore, + dialogParams.bitmapAfter, + false, + false, + this::onUpdateDialogResult); + + PropertyModel model = mDialogManager.getCurrentDialogModel(); + Assert.assertNotNull(model); + Assert.assertTrue(model.get(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY)); + Assert.assertEquals( + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS, + (long) model.get(ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS)); + } }
Original Bug Report
Tapjacking and clickjacking protection missing in WebApkIconNameUpdateDialog
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: The WebApkIconNameUpdateDialog, which serves as a security consent gate when an installed WebAPK updates its name or icon, lacks crucial input-protection properties. This omission leaves the dialog vulnerable to overlay/tapjacking and double-tap input-timing attacks. As a result, a malicious site could potentially bypass user consent to silently spoof an application’s launcher identity on the home screen.
Affected files:
chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.javachrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
Estimated timestamp from git blame: 2021-05-19
Description
When an installed WebAPK (Progressive Web App on Android) changes its launcher name or icon, Chrome displays WebApkIconNameUpdateDialog to request explicit user consent. This prevents a WebAPK from silently masquerading as a high-value application (such as a banking app or system tool) on the device launcher.
However, the PropertyModel for WebApkIconNameUpdateDialog is constructed without the following critical input-protection keys:
ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY(enables touch filtering to block overlay/tapjacking attacks).ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS(enables a temporary tap-delay window to prevent accidental clicks or double-taps).
Because these properties are missing, ModalDialogView defaults both defenses to off (mFilterTouchForSecurity = false and mButtonTapProtectionDurationMs = 0). Consequently, the positive confirmation button is immediately clickable upon dialog presentation and is vulnerable to touch events delivered through obscured/overlay windows.
Potential Impact and Exploitation Scenarios
An attacker who controls an installed WebAPK can potentially orchestrate a silent, persistent launcher-level identity spoof using two attack variants:
-
Variant A: Input-Timing/Double-Tap Attack The attacker’s webpage prompts the user to rapidly tap or spam click on a specific location (e.g., in a simple clicker game). The attacker coordinates a WebAPK update check so that the consent dialog appears immediately prior to a tap in the sequence. Without the protection period, the in-flight tap is instantly processed on the dialog’s “OK” button, automatically approving the identity modification.
-
Variant B: Overlay/Tapjacking Attack A co-installed malicious application with overlay (
SYSTEM_ALERT_WINDOW) permissions draws a deceptive UI directly over the dialog’s action button. Because touch filtering for security is omitted on the dialog, obscured touches are successfully dispatched to Chrome’s underlying dialog button.
Note: These are potential/suggested exploitation steps, as our automated analysis tools do not have the ability to run functional exploit code to confirm.
Affected Code
- File:
chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java- Lines 143-158 in
show(...)(wherePropertyModelis built without the security properties).
- Lines 143-158 in
- File:
chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
Suggested Fix
Ensure that both ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY and ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS are explicitly added to the PropertyModel builder in WebApkIconNameUpdateDialog.java, consistent with other security-sensitive dialogs (such as ExtensionInstallDialogBridge):
mDialogModel =
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, this)
.with(ModalDialogProperties.TITLE, resources, titleId)
.with(ModalDialogProperties.MESSAGE_PARAGRAPH_1, explanation)
.with(ModalDialogProperties.CUSTOM_VIEW, dialogCustomView)
.with(
ModalDialogProperties.BUTTON_STYLES,
ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.ok)
.with(
ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
resources,
R.string.webapp_update_negative_button)
.with(ModalDialogProperties.TITLE_SCROLLABLE, true)
.with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true)
.with(
ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS,
ModalDialogView.LONG_TAP_PROTECTION_DURATION_MS)
.build();
Evaluated with Chrome root at commit: fb72408a8493c46bc75fae1c70d03daec96b3040
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.