Medium firefox Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionWhen a URL was provided in a link querystring parameter, Firefox for Android would follow that URL instead of the correct URL, potentially leading to phishing attacks.<br>*This bug only affects Firefox for Android. Other versions of Firefox are unaffected.*
ComponentCore
Bug ClassLogic Error
Tracker1970151
Fix commit1600b429548b (firefox) +11/-49
CISA KEVNot listed
CreditedRaul Bucata
Disclosed2025-06-24

Files Changed

  • mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt
  • mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
diff --git a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt
index a993e652fbb..a8b97bcc5af 100644
--- a/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt
+++ b/mobile/android/android-components/components/feature/app-links/src/main/java/mozilla/components/feature/app/links/AppLinksUseCases.kt
@@ -26,8 +26,6 @@ import java.net.URISyntaxException
 
 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
 internal const val EXTRA_BROWSER_FALLBACK_URL = "browser_fallback_url"
-internal const val PARAMS_ANDROID_FALLBACK_LINK = "afl" // From https://firebase.google.com/docs/dynamic-links/create-manually
-internal const val PARAMS_FALLBACK_LINK = "link"
 private const val MARKET_INTENT_URI_PACKAGE_PREFIX = "market://details?id="
 
 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -202,24 +200,12 @@ class AppLinksUseCases(
 
             return RedirectData(
                 appIntent = appIntent,
-                fallbackUrl = url.getHierarchicalUrl(),
+                fallbackUrl = null,
                 marketplaceIntent = marketplaceIntent,
                 resolveInfo = resolveInfo,
             )
         }
 
-        private fun String.getHierarchicalUrl(): String? {
-            val fallbackUrlFromUrlParams = this.toUri()
-                .takeIf { it.isHierarchical }
-                ?.let { uriWithParams ->
-                    uriWithParams.getQueryParameter(PARAMS_ANDROID_FALLBACK_LINK)
-                        ?: uriWithParams.getQueryParameter(PARAMS_FALLBACK_LINK)
-                }
-
-            return fallbackUrlFromUrlParams ?: safeParseUri(this, Intent.URI_INTENT_SCHEME)
-                ?.getStringExtra(EXTRA_BROWSER_FALLBACK_URL)
-        }
-
         private fun isPlayStoreURL(url: String): Boolean {
             val playStoreUrlRegex = Regex("https?://play\\.google\\.com/store/.*")
             return url.matches(playStoreUrlRegex)
diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
index 0a963b27b8a..b0b37af15da 100644
--- a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
+++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
@@ -154,7 +154,7 @@ class AppLinksUseCasesTest {
 
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
         assertFalse(redirect.hasMarketplaceIntent())
-        assertTrue(redirect.hasFallback())
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -165,7 +165,7 @@ class AppLinksUseCasesTest {
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
         assertFalse(redirect.hasExternalApp())
         assertTrue(redirect.hasMarketplaceIntent())
-        assertTrue(redirect.hasFallback())
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -348,9 +348,7 @@ class AppLinksUseCasesTest {
 
         val redirect = subject.interceptedAppLinkRedirect(uri)
         assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-
-        assertEquals("http://zxing.org", redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -557,27 +555,6 @@ class AppLinksUseCasesTest {
         assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0)
     }
 
-    @Test
-    fun `WHEN opening a app scheme uri WITH fallback URL THEN use fallback if needed`() {
-        val context = createContext(Triple(appIntentWithPackageAndFallback, appPackage, ""))
-
-        var subject = AppLinksUseCases(context, { false })
-        var redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
-        assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertTrue(redirect.marketplaceIntent != null)
-        assertEquals(redirect.fallbackUrl, "https://example.com")
-
-        AppLinksUseCases.clearRedirectCache()
-        subject = AppLinksUseCases(context, { true })
-        redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
-        assertTrue(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertTrue(redirect.marketplaceIntent != null)
-        assertEquals(redirect.fallbackUrl, "https://example.com")
-        assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0)
-    }
-
     @Test
     fun `WHEN opening a app scheme uri THEN tries to redirect`() {
         val context = createContext(Triple(appIntent, appPackage, ""))
@@ -695,27 +672,26 @@ class AppLinksUseCasesTest {
         val subject = AppLinksUseCases(context, { false })
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndPlayStoreFallback)
         assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertEquals("https://play.google.com/store/abc", redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
-    fun `WHEN A intent WITH android fallback link THEN fallback should be used`() {
+    fun `WHEN A intent WITH android fallback link THEN fallback should NOT be used`() {
         val context = createContext()
         val subject = AppLinksUseCases(context, { true })
 
         val redirect = subject.interceptedAppLinkRedirect(urlWithAndroidFallbackLink)
-        assertNotNull(redirect.fallbackUrl)
-        assertTrue(redirect.hasFallback())
+        assertNull(redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
-    fun `WHEN A intent WITH fallback link THEN fallback should be used`() {
+    fun `WHEN A intent WITH fallback link THEN fallback should NOT be used`() {
         val context = createContext()
         val subject = AppLinksUseCases(context, { true })
 
         val redirect = subject.interceptedAppLinkRedirect(urlWithFallbackLink)
-        assertNotNull(redirect.fallbackUrl)
-        assertTrue(redirect.hasFallback())
+        assertNull(redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 }
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
index 0a963b27b8a..b0b37af15da 100644
--- a/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
+++ b/mobile/android/android-components/components/feature/app-links/src/test/java/mozilla/components/feature/app/links/AppLinksUseCasesTest.kt
@@ -154,7 +154,7 @@ class AppLinksUseCasesTest {
 
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
         assertFalse(redirect.hasMarketplaceIntent())
-        assertTrue(redirect.hasFallback())
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -165,7 +165,7 @@ class AppLinksUseCasesTest {
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
         assertFalse(redirect.hasExternalApp())
         assertTrue(redirect.hasMarketplaceIntent())
-        assertTrue(redirect.hasFallback())
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -348,9 +348,7 @@ class AppLinksUseCasesTest {
 
         val redirect = subject.interceptedAppLinkRedirect(uri)
         assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-
-        assertEquals("http://zxing.org", redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
@@ -557,27 +555,6 @@ class AppLinksUseCasesTest {
         assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0)
     }
 
-    @Test
-    fun `WHEN opening a app scheme uri WITH fallback URL THEN use fallback if needed`() {
-        val context = createContext(Triple(appIntentWithPackageAndFallback, appPackage, ""))
-
-        var subject = AppLinksUseCases(context, { false })
-        var redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
-        assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertTrue(redirect.marketplaceIntent != null)
-        assertEquals(redirect.fallbackUrl, "https://example.com")
-
-        AppLinksUseCases.clearRedirectCache()
-        subject = AppLinksUseCases(context, { true })
-        redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndFallback)
-        assertTrue(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertTrue(redirect.marketplaceIntent != null)
-        assertEquals(redirect.fallbackUrl, "https://example.com")
-        assertTrue(redirect.appIntent?.flags?.and(Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0)
-    }
-
     @Test
     fun `WHEN opening a app scheme uri THEN tries to redirect`() {
         val context = createContext(Triple(appIntent, appPackage, ""))
@@ -695,27 +672,26 @@ class AppLinksUseCasesTest {
         val subject = AppLinksUseCases(context, { false })
         val redirect = subject.interceptedAppLinkRedirect(appIntentWithPackageAndPlayStoreFallback)
         assertFalse(redirect.hasExternalApp())
-        assertTrue(redirect.hasFallback())
-        assertEquals("https://play.google.com/store/abc", redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
-    fun `WHEN A intent WITH android fallback link THEN fallback should be used`() {
+    fun `WHEN A intent WITH android fallback link THEN fallback should NOT be used`() {
         val context = createContext()
         val subject = AppLinksUseCases(context, { true })
 
         val redirect = subject.interceptedAppLinkRedirect(urlWithAndroidFallbackLink)
-        assertNotNull(redirect.fallbackUrl)
-        assertTrue(redirect.hasFallback())
+        assertNull(redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 
     @Test
-    fun `WHEN A intent WITH fallback link THEN fallback should be used`() {
+    fun `WHEN A intent WITH fallback link THEN fallback should NOT be used`() {
         val context = createContext()
         val subject = AppLinksUseCases(context, { true })
 
         val redirect = subject.interceptedAppLinkRedirect(urlWithFallbackLink)
-        assertNotNull(redirect.fallbackUrl)
-        assertTrue(redirect.hasFallback())
+        assertNull(redirect.fallbackUrl)
+        assertFalse(redirect.hasFallback())
     }
 }
Loading diff…