From 251a3cc4e1a789ff88dd049345631b4abceb7a0e Mon Sep 17 00:00:00 2001 From: Lucas Kivi <125697099+lucas-livefront@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:57:06 -0600 Subject: [PATCH] BIT-1506: block autofill for block listed URIs (#855) --- .../autofill/parser/AutofillParserImpl.kt | 22 +++++-- .../autofill/parser/AutofillParserTests.kt | 60 ++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt index a2d9f4e6c2..ecae82f31d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt @@ -15,6 +15,16 @@ import com.x8bit.bitwarden.data.autofill.util.toAutofillView import com.x8bit.bitwarden.data.autofill.util.website import com.x8bit.bitwarden.data.platform.repository.SettingsRepository +/** + * A list of URIs that should never be autofilled. + */ +private val BLOCK_LISTED_URIS: List = listOf( + "androidapp://android", + "androidapp://com.android.settings", + "androidapp://com.x8bit.bitwarden", + "androidapp://com.oneplus.applocker", +) + /** * The default [AutofillParser] implementation for the app. This is a tool for parsing autofill data * from the OS into domain models. @@ -65,15 +75,19 @@ class AutofillParserImpl( .map { it.autofillViews } .flatten() - // Find the focused view (or indicate there is no fulfillment to be performed.) - val focusedView = autofillViews - .firstOrNull { it.data.isFocused } - ?: return AutofillRequest.Unfillable + // Find the focused view. + val focusedView = autofillViews.firstOrNull { it.data.isFocused } val uri = traversalDataList.buildUriOrNull( assistStructure = assistStructure, ) + val blockListedURIs = settingsRepository.blockedAutofillUris + BLOCK_LISTED_URIS + if (focusedView == null || blockListedURIs.contains(uri)) { + // The view is unfillable if there are no focused views or the URI is block listed. + return AutofillRequest.Unfillable + } + // Choose the first focused partition of data for fulfillment. val partition = when (focusedView) { is AutofillView.Card -> { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt index 10cf42815a..41144de71d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt @@ -63,6 +63,7 @@ class AutofillParserTests { private val inlinePresentationSpecs: List = mockk() private val settingsRepository: SettingsRepository = mockk { every { isInlineAutofillEnabled } answers { mockIsInlineAutofillEnabled } + every { blockedAutofillUris } returns emptyList() } private var mockIsInlineAutofillEnabled = true @@ -483,6 +484,57 @@ class AutofillParserTests { } } + @Test + fun `parse should skip block listed URIs Login when a Login view is focused`() { + // Setup all tests + setupAssistStructureWithAllAutofillViewTypes() + val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( + data = AutofillView.Data( + autofillId = cardAutofillId, + isFocused = true, + ), + ) + val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( + data = AutofillView.Data( + autofillId = loginAutofillId, + isFocused = true, + ), + ) + val remoteBlockList = listOf( + "blockListedUri.com", + "blockListedAgainUri.com", + ) + every { cardViewNode.toAutofillView() } returns cardAutofillView + every { loginViewNode.toAutofillView() } returns loginAutofillView + every { settingsRepository.blockedAutofillUris } returns remoteBlockList + + // A function for asserting that a block listed URI results in an unfillable request. + fun testBlockListedUri(blockListedUri: String) { + // Setup + every { + any>().buildUriOrNull(assistStructure) + } returns blockListedUri + + // Test + val actual = parser.parse( + autofillAppInfo = autofillAppInfo, + fillRequest = fillRequest, + ) + + // Verify + assertEquals(AutofillRequest.Unfillable, actual) + } + + // Test all block listed URIs + BLOCK_LISTED_URIS.forEach(::testBlockListedUri) + remoteBlockList.forEach(::testBlockListedUri) + + // Verify all tests + verify(exactly = BLOCK_LISTED_URIS.size + remoteBlockList.size) { + any>().buildUriOrNull(assistStructure) + } + } + /** * Setup [assistStructure] to return window nodes with each [AutofillView] type (card and login) * so we can test how different window node configurations produce different partitions. @@ -494,7 +546,13 @@ class AutofillParserTests { } } +private val BLOCK_LISTED_URIS: List = listOf( + "androidapp://android", + "androidapp://com.android.settings", + "androidapp://com.x8bit.bitwarden", + "androidapp://com.oneplus.applocker", +) private const val ID_PACKAGE: String = "com.x8bit.bitwarden" private const val MAX_INLINE_SUGGESTION_COUNT: Int = 42 -private const val URI: String = "androidapp://com.x8bit.bitwarden" +private const val URI: String = "androidapp://com.google" private const val WEBSITE: String = "https://www.google.com"