fix: Autofill pending intent bypass #2238

This commit is contained in:
J-Jamet
2025-10-24 14:43:42 +02:00
parent d6dc75961b
commit d2549d61d6
4 changed files with 115 additions and 17 deletions

View File

@@ -39,9 +39,12 @@ import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.utils.LOCK_ACTION import com.kunzisoft.keepass.utils.LOCK_ACTION
import com.kunzisoft.keepass.utils.getEnum
import com.kunzisoft.keepass.utils.getEnumExtra import com.kunzisoft.keepass.utils.getEnumExtra
import com.kunzisoft.keepass.utils.getParcelableCompat
import com.kunzisoft.keepass.utils.getParcelableExtraCompat import com.kunzisoft.keepass.utils.getParcelableExtraCompat
import com.kunzisoft.keepass.utils.getParcelableList import com.kunzisoft.keepass.utils.getParcelableList
import com.kunzisoft.keepass.utils.putEnum
import com.kunzisoft.keepass.utils.putEnumExtra import com.kunzisoft.keepass.utils.putEnumExtra
import com.kunzisoft.keepass.utils.putParcelableList import com.kunzisoft.keepass.utils.putParcelableList
import java.io.IOException import java.io.IOException
@@ -159,10 +162,21 @@ object EntrySelectionHelper {
return this return this
} }
fun Bundle.addSearchInfo(searchInfo: SearchInfo?): Bundle {
searchInfo?.let {
putParcelable(KEY_SEARCH_INFO, it)
}
return this
}
fun Intent.retrieveSearchInfo(): SearchInfo? { fun Intent.retrieveSearchInfo(): SearchInfo? {
return getParcelableExtraCompat(KEY_SEARCH_INFO) return getParcelableExtraCompat(KEY_SEARCH_INFO)
} }
fun Bundle.getSearchInfo(): SearchInfo? {
return getParcelableCompat(KEY_SEARCH_INFO)
}
fun Intent.addRegisterInfo(registerInfo: RegisterInfo?): Intent { fun Intent.addRegisterInfo(registerInfo: RegisterInfo?): Intent {
registerInfo?.let { registerInfo?.let {
putExtra(KEY_REGISTER_INFO, it) putExtra(KEY_REGISTER_INFO, it)
@@ -170,10 +184,21 @@ object EntrySelectionHelper {
return this return this
} }
fun Bundle.addRegisterInfo(registerInfo: RegisterInfo?): Bundle {
registerInfo?.let {
putParcelable(KEY_REGISTER_INFO, it)
}
return this
}
fun Intent.retrieveRegisterInfo(): RegisterInfo? { fun Intent.retrieveRegisterInfo(): RegisterInfo? {
return getParcelableExtraCompat(KEY_REGISTER_INFO) return getParcelableExtraCompat(KEY_REGISTER_INFO)
} }
fun Bundle.getRegisterInfo(): RegisterInfo? {
return getParcelableCompat(KEY_REGISTER_INFO)
}
fun Intent.removeInfo() { fun Intent.removeInfo() {
removeExtra(KEY_SEARCH_INFO) removeExtra(KEY_SEARCH_INFO)
removeExtra(KEY_REGISTER_INFO) removeExtra(KEY_REGISTER_INFO)
@@ -184,8 +209,17 @@ object EntrySelectionHelper {
return this return this
} }
fun Bundle.addSpecialMode(specialMode: SpecialMode): Bundle {
this.putEnum(KEY_SPECIAL_MODE, specialMode)
return this
}
fun Intent.retrieveSpecialMode(): SpecialMode { fun Intent.retrieveSpecialMode(): SpecialMode {
return getEnumExtra<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT return this.getEnumExtra<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT
}
fun Bundle.getSpecialMode(): SpecialMode {
return this.getEnum<SpecialMode>(KEY_SPECIAL_MODE) ?: SpecialMode.DEFAULT
} }
fun Intent.addTypeMode(typeMode: TypeMode): Intent { fun Intent.addTypeMode(typeMode: TypeMode): Intent {
@@ -236,7 +270,7 @@ object EntrySelectionHelper {
} }
/** /**
* Retrieve nodes ids from [intent] and get the corresponding entry info list in [database] * Retrieve nodes ids from intent and get the corresponding entry info list in [database]
*/ */
fun Intent.retrieveAndRemoveEntries(database: ContextualDatabase): List<EntryInfo> { fun Intent.retrieveAndRemoveEntries(database: ContextualDatabase): List<EntryInfo> {
val nodesIds = retrieveNodesIds() val nodesIds = retrieveNodesIds()

View File

@@ -38,10 +38,14 @@ import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addRegisterInfo import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addRegisterInfo
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchInfo import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchInfo
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getRegisterInfo
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getSearchInfo
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.getSpecialMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
import com.kunzisoft.keepass.credentialprovider.SpecialMode import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.retrieveAutofillComponent
import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel
import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel import com.kunzisoft.keepass.credentialprovider.viewmodel.CredentialLauncherViewModel
import com.kunzisoft.keepass.database.ContextualDatabase import com.kunzisoft.keepass.database.ContextualDatabase
@@ -75,6 +79,14 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// To apply the bypass https://github.com/Kunzisoft/KeePassDX/issues/2238
// before managing intent in super class
intent.retrieveSelectionBundle()?.apply {
intent.addSpecialMode(getSpecialMode())
intent.addSearchInfo(getSearchInfo())
intent.addRegisterInfo(getRegisterInfo())
intent.addAutofillComponent(retrieveAutofillComponent())
}
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
autofillLauncherViewModel.initialize() autofillLauncherViewModel.initialize()
lifecycleScope.launch { lifecycleScope.launch {
@@ -171,23 +183,32 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
companion object { companion object {
private const val KEY_PENDING_INTENT_BUNDLE = "com.kunzisoft.keepass.extra.BUNDLE"
private val TAG = AutofillLauncherActivity::class.java.name private val TAG = AutofillLauncherActivity::class.java.name
fun Intent.retrieveSelectionBundle(): Bundle? {
return this.getBundleExtra(KEY_PENDING_INTENT_BUNDLE)
}
fun getPendingIntentForSelection( fun getPendingIntentForSelection(
context: Context, context: Context,
searchInfo: SearchInfo? = null, searchInfo: SearchInfo? = null,
autofillComponent: AutofillComponent autofillComponent: AutofillComponent
): PendingIntent? { ): PendingIntent? {
try { try {
return PendingIntent.getActivity( // Doesn't work with direct extra Parcelable in Android 11 (don't know why?)
context, // https://github.com/Kunzisoft/KeePassDX/issues/2238
randomRequestCode(),
// Doesn't work with direct extra Parcelable (don't know why?)
// Wrap into a bundle to bypass the problem // Wrap into a bundle to bypass the problem
Intent(context, AutofillLauncherActivity::class.java).apply { val tempBundle = Bundle().apply {
addSpecialMode(SpecialMode.SELECTION) addSpecialMode(SpecialMode.SELECTION)
addSearchInfo(searchInfo) addSearchInfo(searchInfo)
addAutofillComponent(autofillComponent) addAutofillComponent(autofillComponent)
}
return PendingIntent.getActivity(
context,
randomRequestCode(),
Intent(context, AutofillLauncherActivity::class.java).apply {
putExtra(KEY_PENDING_INTENT_BUNDLE, tempBundle)
}, },
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
@@ -206,12 +227,16 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
registerInfo: RegisterInfo registerInfo: RegisterInfo
): PendingIntent? { ): PendingIntent? {
try { try {
// Bypass intent issue
val tempBundle = Bundle().apply {
addSpecialMode(SpecialMode.REGISTRATION)
addRegisterInfo(registerInfo)
}
return PendingIntent.getActivity( return PendingIntent.getActivity(
context, context,
randomRequestCode(), randomRequestCode(),
Intent(context, AutofillLauncherActivity::class.java).apply { Intent(context, AutofillLauncherActivity::class.java).apply {
addSpecialMode(SpecialMode.REGISTRATION) putExtra(KEY_PENDING_INTENT_BUNDLE, tempBundle)
addRegisterInfo(registerInfo)
}, },
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT

View File

@@ -27,6 +27,7 @@ import android.content.Intent
import android.graphics.BlendMode import android.graphics.BlendMode
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import android.os.Bundle
import android.service.autofill.Dataset import android.service.autofill.Dataset
import android.service.autofill.Field import android.service.autofill.Field
import android.service.autofill.FillResponse import android.service.autofill.FillResponse
@@ -53,6 +54,7 @@ import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.settings.AutofillSettingsActivity import com.kunzisoft.keepass.settings.AutofillSettingsActivity
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
import com.kunzisoft.keepass.utils.getParcelableCompat
import com.kunzisoft.keepass.utils.getParcelableExtraCompat import com.kunzisoft.keepass.utils.getParcelableExtraCompat
import java.io.IOException import java.io.IOException
import kotlin.math.min import kotlin.math.min
@@ -64,7 +66,8 @@ object AutofillHelper {
private const val EXTRA_BASE_STRUCTURE = "com.kunzisoft.keepass.autofill.BASE_STRUCTURE" private const val EXTRA_BASE_STRUCTURE = "com.kunzisoft.keepass.autofill.BASE_STRUCTURE"
private const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST" private const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST"
fun Intent.addAutofillComponent(autofillComponent: AutofillComponent) { fun Intent.addAutofillComponent(autofillComponent: AutofillComponent?): Intent {
autofillComponent?.let {
this.putExtra(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure) this.putExtra(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
autofillComponent.compatInlineSuggestionsRequest?.let { autofillComponent.compatInlineSuggestionsRequest?.let {
@@ -72,12 +75,41 @@ object AutofillHelper {
} }
} }
} }
return this
}
fun Intent.retrieveAutofillComponent(): AutofillComponent? { fun Intent.retrieveAutofillComponent(): AutofillComponent? {
getParcelableExtraCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure -> this.getParcelableExtraCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure ->
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
AutofillComponent(assistStructure, AutofillComponent(
getParcelableExtraCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST)) assistStructure,
this.getParcelableExtraCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST))
} else {
AutofillComponent(assistStructure, null)
}
}
return null
}
fun Bundle.addAutofillComponent(autofillComponent: AutofillComponent?): Bundle {
autofillComponent?.let {
this.putParcelable(EXTRA_BASE_STRUCTURE, autofillComponent.assistStructure)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
autofillComponent.compatInlineSuggestionsRequest?.let {
this.putParcelable(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
}
}
}
return this
}
fun Bundle.retrieveAutofillComponent(): AutofillComponent? {
this.getParcelableCompat<AssistStructure>(EXTRA_BASE_STRUCTURE)?.let { assistStructure ->
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
AutofillComponent(
assistStructure,
this.getParcelableCompat(EXTRA_INLINE_SUGGESTIONS_REQUEST)
)
} else { } else {
AutofillComponent(assistStructure, null) AutofillComponent(assistStructure, null)
} }

View File

@@ -82,6 +82,13 @@ inline fun <reified E : Parcelable> Bundle.getParcelableList(key: String?): Muta
else -> @Suppress("DEPRECATION", "UNCHECKED_CAST") (getParcelableArray(key) as? Array<E>)?.toMutableList() else -> @Suppress("DEPRECATION", "UNCHECKED_CAST") (getParcelableArray(key) as? Array<E>)?.toMutableList()
} }
inline fun <reified T : Enum<T>> Bundle.putEnum(key: String?, value: T?) =
putString(key, value?.name)
inline fun <reified T : Enum<T>> Bundle.getEnum(key: String?): T? {
return getString(key)?.let { enumValueOf<T>(it) }
}
// -------- Parcel -------- // -------- Parcel --------
inline fun <reified T : Parcelable> Parcel.readParcelableCompat(): T? = when { inline fun <reified T : Parcelable> Parcel.readParcelableCompat(): T? = when {