1package com.peachpasswords.qa23import android.content.ClipData4import android.content.ClipboardManager5import android.content.Context6import androidx.test.ext.junit.runners.AndroidJUnit47import androidx.test.platform.app.InstrumentationRegistry8import androidx.test.uiautomator.By9import androidx.test.uiautomator.UiDevice10import androidx.test.uiautomator.Until11import java.io.BufferedReader12import java.io.InputStreamReader13import org.junit.Assert.assertTrue14import org.junit.Test15import org.junit.runner.RunWith161718192021222324252627@RunWith(AndroidJUnit4::class)28class QaFormTwoFactorChipTest {2930 @Test fun framework_issues_fill_request_for_form_with_2fa_field() {31 shell("am force-stop com.peachpasswords.debug.test")32 shell("am start -n com.peachpasswords.debug.test/com.peachpasswords.qa.QaFormActivity")33 Thread.sleep(2000)34 focusField("Username field")35 Thread.sleep(4000)36 val dump = shell("dumpsys autofill")37 val mentions = dump.lineSequence()38 .filter { it.contains("com.peachpasswords.debug.test") }39 .take(10)40 .joinToString("\n")41 assertTrue(42 "no autofill session for the 2FA form -- framework never issued a fill request.\n" +43 "dumpsys excerpt:\n$mentions",44 mentions.isNotEmpty(),45 )46 }4748 49 @Test50 fun malicious_companion_spoofed_web_domain_does_not_copy_totp() {51 shell("am force-stop com.peachpasswords.debug.test")52 val clipboard = InstrumentationRegistry.getInstrumentation().targetContext53 .getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager54 clipboard.setPrimaryClip(ClipData.newPlainText("sentinel", "user-owned-clipboard"))55 shell("logcat -c")56 shell("am start -n com.peachpasswords.debug.test/com.peachpasswords.qa.QaSpoofedWebViewActivity")57 Thread.sleep(2000)58 focusField("Spoofed username field")59 Thread.sleep(4000)60 val logcat = shell("logcat -d -s PeachAutofill:D")61 assertTrue(62 "spoofed native form did not exercise Peach's unverified-web denial path:\n$logcat",63 logcat.contains("refused unverified web origin"),64 )65 assertTrue(66 "malicious autofill origin must not overwrite the user's clipboard",67 clipboard.primaryClip?.getItemAt(0)?.text?.toString() == "user-owned-clipboard",68 )69 }7071 private fun shell(command: String): String {72 val instrumentation = InstrumentationRegistry.getInstrumentation()73 instrumentation.uiAutomation.executeShellCommand(command).use { pfd ->74 BufferedReader(InputStreamReader(android.os.ParcelFileDescriptor.AutoCloseInputStream(pfd))).use {75 return it.readText()76 }77 }78 }79 8081828384858687 @Test fun framework_issues_fill_request_when_focused_on_2fa_field_only() {88 shell("am force-stop com.peachpasswords.debug.test")89 shell("am start -n com.peachpasswords.debug.test/com.peachpasswords.qa.QaFormActivity")90 Thread.sleep(2000)91 92 93 focusField("2FA code field")94 Thread.sleep(4000)95 val dump = shell("dumpsys autofill")96 val mentions = dump.lineSequence()97 .filter { it.contains("com.peachpasswords.debug.test") }98 .take(10)99 .joinToString("\n")100 assertTrue(101 "no autofill session when 2FA field is focused -- picker collision regression.\n" +102 "dumpsys excerpt:\n$mentions",103 mentions.isNotEmpty(),104 )105 }106107 private fun focusField(contentDescription: String) {108 val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())109 val selector = By.desc(contentDescription)110 assertTrue("field not found: $contentDescription", device.wait(Until.hasObject(selector), 5_000))111 device.findObject(selector).click()112 }113}