Medium watch CVSS 5.4 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
5.4
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Toolbar
DescriptionInappropriate implementation in Toolbar
ComponentToolbar
Sub-componentMobar
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker437147699
Fix commitb583fe9162bd (chromium/src) +40/-2
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedKhalil Zhani
Reported2025-08-07 · bounty $5000 (per vendor advisory)
Fix landed2025-08-12 (public git)
Disclosed2025-09-02
Patch gap21 days — fix public in source before the advisory

Background

Mini origin bar ("mobar")
an Android toolbar mode in MiniOriginBarController.java. When the browser controls are at the bottom (ControlsPosition.BOTTOM) and a form field is focused with the keyboard up, the location bar is reduced to show only the origin (setShowOriginOnly) and is moved up to sit above the IME.
`WindowInsetsAnimationCompat`
the AndroidX callback API (onPrepare, onStart, onProgress, onEnd) that reports keyboard (IME) show and hide animations. The controller uses it to compute the toolbar translation each frame.
`mAnimation` / `mAnimationInProgress`
the controller’s record of which inset animation it is tracking and whether one is running. onProgress updates are interpreted against this tracked animation.
Animation replacement
Android can start a new IME animation before the previous one finishes, for example a “false start” that begins showing the keyboard and then hides it.

Root Cause Analysis

In the animation listener’s onStart, the old code returned at once whenever animation != mAnimation. Any newly started animation that was not the one the controller had tracked from onPrepare was ignored. When a second IME animation replaced the first mid-flight, mAnimation still pointed at the stale animation, so later progress updates were applied “as if the old one is still running”. The commit message says this miscalculates the translation of the bottom controls, the container that holds the origin display.

The patch relaxes the identity check: a mismatched animation is still ignored if its getTypeMask() lacks WindowInsetsCompat.Type.ime(), but a new IME animation is now adopted (mAnimation = animation), and mAnimationInProgress = true is set for it. The regression test testAnimationReplacement starts a show animation, replaces it with a hide animation, and asserts that the control container translation tracks the current keyboard height (-currentKeyboardHeight).

Key insight
The controller kept tracking only the first IME animation and dropped a replacement in onStart, so the toolbar position stopped matching the keyboard. The fix adopts any new IME-typed animation as the current one.

Attack Path

  1. Bottom-toolbar configuration The victim uses Chrome on Android with the toolbar at the bottom, so the mini origin bar logic is active, as in the test’s ControlsPosition.BOTTOM setup.
  2. Focus a form field A web page focuses an editable field, and the keyboard begins to animate in.
  3. Force a replacement animation The page quickly changes focus, for example focusing and then blurring, so a hide animation starts before the show animation finishes (the page-driven trigger is inferred).
  4. Mispositioned origin display The controller ignores the new animation and computes the control-container translation from stale state. The origin bar can end up misplaced relative to the keyboard, for example covered or offset, while the page keeps drawing the visible content. That URL visibility or spoofing effect is (inferred) from the Toolbar component and the translation bug.

Impact Assessment

The bug is a browser UI state error on Android Chrome. It can leave the bottom toolbar’s origin display mispositioned during keyboard transitions, which the advisory classes as an inappropriate implementation in Toolbar. Any security effect would be weaker origin indication or UI spoofing (inferred). It needs the bottom-toolbar configuration, a focused form field, and timing that produces a replaced IME animation. The diff does not show whether the mini origin bar is enabled by default or behind a feature flag. Ordinary web content can drive focus changes and needs no compromised renderer. The vendor rates it medium severity, and the advisory does not report exploitation in the wild.

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
modified

Files Changed

  • chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
  • chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java

Audit Directions

  • Stale "current animation" tracking
    Check other WindowInsetsAnimationCompat.Callback users in Chrome Android, such as the keyboard accessory, bottom sheets and browser controls, for identity checks that drop replacement animations and leave security UI positioned from stale state.
  • Origin and security UI under layout transitions
    Look for code paths where the URL bar or origin chip can be hidden, translated off-screen or covered while page content stays visible and interactive, especially around IME show and hide or fullscreen changes.
  • Page-controlled UI churn
    Treat any focus, scroll or keyboard sequence that a page can drive as adversarial input to browser-chrome animation state machines, and test interrupted and reversed transitions.
From b583fe9162bd751d0c6bd065b4e7da0c76d66c6a Mon Sep 17 00:00:00 2001
From: Patrick Noland <[email protected]>
Date: Tue, 12 Aug 2025 10:46:56 -0700
Subject: [PATCH] [mobar] Handle mid-animation replacement

Although not explicitly documented, it's possible for a new IME
animation to start without the old one finishing, e.g. a "false start"
start-showing-then-hide. If we don't update to reflect the state of the
new animation we mistakenly perform updates as if the old one is still
running which can cause us to e.g. miscalculate the translation.

Bug: 437147699
Change-Id: I56b22c121848acb61410d33b42e82b8c0b05cd1a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6832030
Reviewed-by: Tomasz Wiszkowski <[email protected]>
Commit-Queue: Patrick Noland <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1500279}
---

diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
index 567e5bd..04f67ba2 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarController.java
@@ -524,7 +524,10 @@
         @Override
         public void onStart(WindowInsetsAnimationCompat animation, BoundsCompat bounds) {
             if (animation != mAnimation) {
-                return;
+                if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) == 0) {
+                    return;
+                }
+                mAnimation = animation;
             }
 
             mAnimationInProgress = true;
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
index 451ceb7..ce3f59aa 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/MiniOriginBarControllerTest.java
@@ -593,5 +593,40 @@
         verify(mLocationBar, never()).setShowOriginOnly(anyBoolean());
     }
 
-    // show again, start, finish showing (predictive back)
+    @Test
+    public void testAnimationReplacement() {
+        doReturn(ControlsPosition.BOTTOM).when(mBrowserControlsSizer).getControlsPosition();
+        mMiniOriginBarController.onControlsPositionChanged(ControlsPosition.BOTTOM);
+        final MiniOriginWindowInsetsAnimationListener animationListener =
+                mMiniOriginBarController.getAnimationListenerForTesting();
+
+        final int finalKeyboardHeight = 100;
+        final BoundsCompat bounds =
+                new BoundsCompat(Insets.NONE, Insets.of(0, 0, 0, finalKeyboardHeight));
+        mIsFormFieldFocused.onNodeAttributeUpdated(true, false);
+
+        animationListener.onPrepare(mImeAnimation);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(true);
+        animationListener.onStart(mImeAnimation, bounds);
+
+        int currentKeyboardHeight = 50;
+        WindowInsetsCompat insets =
+                new WindowInsetsCompat.Builder()
+                        .setInsets(
+                                WindowInsetsCompat.Type.ime(),
+                                Insets.of(0, 0, 0, currentKeyboardHeight))
+                        .build();
+        mImeAnimation.setFraction(0.5f);
+        animationListener.onProgress(insets, Collections.singletonList(mImeAnimation));
+
+        // Start a second animation, this time hiding the keyboard.
+        final WindowInsetsAnimationCompat secondImeAnimation =
+                new WindowInsetsAnimationCompat(WindowInsetsCompat.Type.ime(), null, 160);
+        mKeyboardVisibilityDelegate.setVisibilityForTests(false);
+        animationListener.onStart(secondImeAnimation, bounds);
+
+        secondImeAnimation.setFraction(0.5f);
+        animationListener.onProgress(insets, Collections.singletonList(secondImeAnimation));
+        assertEquals(-currentKeyboardHeight, (int) mControlContainerTranslationSupplier.get());
+    }
 }
Loading diff…

Original Bug Report

reported by [email protected]

Chrome on Android: Spoof issue triggered by bottom address bar

Steps to reproduce the problem

  1. Navigate to https://lbstyle.github.io/sandbox.html
  2. Tap inside the input field
  3. Tap docwrite1

Problem Description

Note: This is very similar to issue 379652406 and issue 40064686.

The omnibox disappears, and the attack can trick the user into thinking they are seeing a fake omnibox of a secure website.

Summary

Chrome on Android: Spoof issue triggered by bottom address bar

Additional Data

Category: Security
Chrome Channel: Canary
Regression: N/A \

View on issue tracker