Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSpoofing issue in the Toolbar component in Firefox for Android
ComponentCore
Bug ClassLogic Error
Tracker2018513
Fix commitaa18978e5e7e (firefox) +56/-28
CISA KEVNot listed
Creditedyfbug123
Disclosed2026-05-19

Files Changed

  • mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrl.kt
  • mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrl.kt b/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrl.kt
index 7ceb0b6d3ea..5e7c5fefad1 100644
--- a/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrl.kt
+++ b/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrl.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.LocalDensity
@@ -171,7 +172,12 @@ private fun Modifier.focusTextIndexRange(
                     .drawWithContent {
                         drawContent()
 
-                        val brush = createDomainHighlightBrush(text, highlightRange, scrollState.value, fadeFraction)
+                        val brush = createUrlFadeBrush(
+                            scrolledPixels = scrollState.value,
+                            maxScrollPixels = scrollState.maxValue,
+                            viewportSize = scrollState.viewportSize,
+                            fadeFraction = fadeFraction
+                        )
 
                         drawRect(
                             brush = brush,
@@ -223,21 +229,26 @@ internal fun computeDomainEndScrollValue(
 }
 
 @VisibleForTesting
-internal fun createDomainHighlightBrush(
-    text: String,
-    highlightRange: Pair<Int, Int>?,
+internal fun createUrlFadeBrush(
     scrolledPixels: Int,
+    maxScrollPixels: Int,
+    viewportSize: Int,
     fadeFraction: Float,
 ): Brush {
-    val brush = when {
-        // Don't fade the start if the text is not scrolled to fit the highlighted domain.
-         scrolledPixels == 0 -> Brush.horizontalGradient(
+    val fadeWidthPixels = viewportSize * fadeFraction
+    val needsLeftFade = scrolledPixels > 0
+    val remainingScroll = maxScrollPixels - scrolledPixels
+    val needsRightFade = remainingScroll > fadeWidthPixels
+
+    return when {
+        !needsLeftFade && !needsRightFade -> SolidColor(Color.Black)
+
+        !needsLeftFade && needsRightFade -> Brush.horizontalGradient(
             (1f - fadeFraction) to Color.Black,
             1f to Color.Transparent,
         )
 
-        // Don't fade the end if the highlight is also at the end of the text.
-        (highlightRange?.second ?: Int.MIN_VALUE) >= text.lastIndex -> Brush.horizontalGradient(
+        needsLeftFade && !needsRightFade -> Brush.horizontalGradient(
             0f to Color.Transparent,
             fadeFraction to Color.Black,
         )
@@ -251,7 +262,6 @@ internal fun createDomainHighlightBrush(
             ),
         )
     }
-    return brush
 }
 
 @Composable
diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt b/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
index 2919b133319..e48b327aaa3 100644
--- a/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
+++ b/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
@@ -9,6 +9,7 @@ import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import mozilla.components.support.test.mock
@@ -91,43 +92,55 @@ class HighlightedDomainUrlTest {
     }
 
     @Test
-    fun `GIVEN the start of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "pagedomain.com/testing"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL does not overflow the available space WHEN creating the fading brush THEN set no fading`() {
+        val expected = SolidColor(Color.Black)
+
+        val result = createUrlFadeBrush(
+            scrolledPixels = 0,
+            maxScrollPixels = 0,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
+
+        assertEquals(expected, result)
+    }
+
+    @Test
+    fun `GIVEN the displayed URL only overflows to the right WHEN creating the fading brush THEN fade the right edge`() {
         val expected = Brush.horizontalGradient(
             0.5f to Color.Black,
             1f to Color.Transparent,
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 0,
+            maxScrollPixels = 600,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
 
     @Test
-    fun `GIVEN the end of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "subdomain.pagedomain.com"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL only overflows to the left WHEN creating the fading brush THEN fade the left edge`() {
         val expected = Brush.horizontalGradient(
             0f to Color.Transparent,
             0.5f to Color.Black,
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 600,
+            maxScrollPixels = 600,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
 
     @Test
-    fun `GIVEN the middle of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "subdomain.pagedomain.com/testing"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL overflows on both sides WHEN creating the fading brush THEN fade both edges`() {
         val expected = Brush.horizontalGradient(
             colorStops = arrayOf(
                 0f to Color.Transparent,
@@ -137,7 +150,12 @@ class HighlightedDomainUrlTest {
             ),
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 100,
+            maxScrollPixels = 1200,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt b/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
index 2919b133319..e48b327aaa3 100644
--- a/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
+++ b/mobile/android/android-components/components/compose/browser-toolbar/src/test/java/mozilla/components/compose/browser/toolbar/ui/HighlightedDomainUrlTest.kt
@@ -9,6 +9,7 @@ import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import mozilla.components.support.test.mock
@@ -91,43 +92,55 @@ class HighlightedDomainUrlTest {
     }
 
     @Test
-    fun `GIVEN the start of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "pagedomain.com/testing"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL does not overflow the available space WHEN creating the fading brush THEN set no fading`() {
+        val expected = SolidColor(Color.Black)
+
+        val result = createUrlFadeBrush(
+            scrolledPixels = 0,
+            maxScrollPixels = 0,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
+
+        assertEquals(expected, result)
+    }
+
+    @Test
+    fun `GIVEN the displayed URL only overflows to the right WHEN creating the fading brush THEN fade the right edge`() {
         val expected = Brush.horizontalGradient(
             0.5f to Color.Black,
             1f to Color.Transparent,
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 0,
+            maxScrollPixels = 600,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
 
     @Test
-    fun `GIVEN the end of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "subdomain.pagedomain.com"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL only overflows to the left WHEN creating the fading brush THEN fade the left edge`() {
         val expected = Brush.horizontalGradient(
             0f to Color.Transparent,
             0.5f to Color.Black,
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 600,
+            maxScrollPixels = 600,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
 
     @Test
-    fun `GIVEN the middle of the URL should be highlighted THEN get an appropriate highlight brush`() {
-        val url = "subdomain.pagedomain.com/testing"
-        scrollState.stubScrollInfo(url)
-        val highlightRange = computeHighlightRange(url, "pagedomain.com")
-        val scrolledPixels = computeDomainEndScrollValue(url, highlightRange)
+    fun `GIVEN the displayed URL overflows on both sides WHEN creating the fading brush THEN fade both edges`() {
         val expected = Brush.horizontalGradient(
             colorStops = arrayOf(
                 0f to Color.Transparent,
@@ -137,7 +150,12 @@ class HighlightedDomainUrlTest {
             ),
         )
 
-        val result = createDomainHighlightBrush(url, highlightRange, scrolledPixels, 0.5f)
+        val result = createUrlFadeBrush(
+            scrolledPixels = 100,
+            maxScrollPixels = 1200,
+            viewportSize = viewPortWidth.toInt(),
+            fadeFraction = 0.5f,
+        )
 
         assertEquals(expected, result)
     }
Loading diff…