Low firefox Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactlow
DescriptionClickjacking issue in the Downloads component in Firefox for Android
ComponentCore
Bug ClassLogic Error
Tracker2049034
Fix commit5bf9b39dde43 (firefox) +40/-6
CISA KEVNot listed
CreditedHafiizh
Disclosed2026-08-18

Changed Functions

FunctionChangeNotes
Transition
mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
modified
RenameAndChangeLocationDialogFragment
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
modified

Files Changed

  • mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
diff --git a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
index e3dc093d419..bde1643a9a4 100644
--- a/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
+++ b/mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
@@ -29,6 +29,7 @@ import androidx.test.espresso.intent.matcher.IntentMatchers
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiSelector
 import androidx.test.uiautomator.Until
+import mozilla.components.support.ktx.util.PromptAbuserDetector
 import org.hamcrest.CoreMatchers.allOf
 import org.mozilla.fenix.R
 import org.mozilla.fenix.compose.snackbar.SNACKBAR_TEST_TAG
@@ -257,7 +258,9 @@ class DownloadRobot(private val composeTestRule: ComposeTestRule) {
     class Transition(private val composeTestRule: ComposeTestRule) {
         fun clickDownload(composeTestRule: ComposeTestRule, interact: DownloadRobot.() -> Unit): Transition {
             Log.i(TAG, "clickDownload: Trying to click the \"Download\" download prompt button")
+            PromptAbuserDetector.validationsEnabled = false
             composeTestRule.downloadButton().performClick()
+            PromptAbuserDetector.validationsEnabled = true
             Log.i(TAG, "clickDownload: Clicked the \"Download\" download prompt button")
 
             DownloadRobot(composeTestRule).interact()
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
index 61af9284515..0f7a30b76e5 100644
--- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
+++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
@@ -20,6 +20,8 @@ import androidx.fragment.app.DialogFragment
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import mozilla.components.concept.base.crash.Breadcrumb
 import mozilla.components.support.base.log.logger.Logger
+import mozilla.components.support.ktx.util.PromptAbuserDetector
+import mozilla.components.support.utils.OnEnterAnimationCompleteListener
 import org.mozilla.fenix.R
 import org.mozilla.fenix.ext.components
 import org.mozilla.fenix.ext.requireComponents
@@ -38,10 +40,12 @@ import org.mozilla.fenix.theme.FirefoxTheme
  *
  * The callback [onConfirmSave] is invoked with the final file name and directory path.
  */
-class RenameAndChangeLocationDialogFragment : DialogFragment() {
+class RenameAndChangeLocationDialogFragment : DialogFragment(), OnEnterAnimationCompleteListener {
     private val logger = Logger("RenameAndChangeLocationDialogFragment")
     private val safeArguments get() = requireNotNull(arguments)
 
+    private val promptAbuserDetector = PromptAbuserDetector(TIME_SHOWN_OFFSET_MILLIS)
+
     internal val fileName: String
         get() = safeArguments.getString(KEY_FILE_NAME, "")
 
@@ -75,6 +79,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
         }
     }
 
+    override fun onResume() {
+        super.onResume()
+        promptAbuserDetector.start()
+    }
+
+    override fun onEnterAnimationComplete() {
+        promptAbuserDetector.start()
+    }
+
     override fun onCancel(dialog: DialogInterface) {
         super.onCancel(dialog)
         onCancel()
@@ -99,6 +112,8 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
 
         val composeView = createComposeView()
 
+        promptAbuserDetector.start()
+
         return MaterialAlertDialogBuilder(requireContext())
             .setView(composeView)
             .create()
@@ -144,11 +159,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
                             directoryLauncher.launch(null)
                         },
                         onConfirm = {
-                            onConfirmSave(
-                                dialogState.fileName,
-                                dialogState.directoryPath,
-                            )
-                            dismiss()
+                            if (promptAbuserDetector.areDialogsBeingAbused()) {
+                                promptAbuserDetector.updateJSDialogAbusedState()
+                            } else {
+                                onConfirmSave(
+                                    dialogState.fileName,
+                                    dialogState.directoryPath,
+                                )
+                                dismiss()
+                            }
                         },
                         onCancel = {
                             onCancel()
@@ -182,6 +201,7 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
         private const val KEY_DIRECTORY_PATH = "directory_path"
         private const val KEY_CONTENT_SIZE = "content_size"
         const val RENAME_AND_CHANGE_LOCATION_DIALOG_TAG = "RENAME_AND_CHANGE_LOCATION_DIALOG_TAG"
+        private const val TIME_SHOWN_OFFSET_MILLIS = 500
 
         /**
          * Creates a new instance of [RenameAndChangeLocationDialogFragment].
@@ -203,3 +223,14 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
         }
     }
 }
+
+/**
+ * Starts (or restarts) the time-based check without increasing the "click count".
+ *
+ * Makes it safe to call from multiple/successive lifecycle methods, without running into the risk
+ * of triggering the more restrictive count-based protection on the 1st click (or even before it).
+ */
+private fun PromptAbuserDetector.start() {
+    resetJSAlertAbuseState()
+    updateJSDialogAbusedState()
+}
Loading diff…