AutofillSaveTest.kt

4.5 KB · raw

1package com.peachpasswords.autofill23import com.peachpasswords.vault.VaultEntry4import org.junit.Assert.assertEquals5import org.junit.Assert.assertNull6import org.junit.Assert.assertTrue7import org.junit.Test89class AutofillSaveTest {1011    private fun entry(12        title: String,13        url: String,14        username: String? = "june",15        password: String = "old",16    ) = VaultEntry(17        id = title,18        type = "login",19        title = title,20        username = username,21        password = password,22        url = url,23        createdAt = 1,24        modifiedAt = 1,25    )2627    @Test28    fun autofillSaveDraft_requiresOriginAndPassword() {29        assertNull(autofillSaveDraft(ParsedStructure(webDomain = "example.com")))30        assertNull(autofillSaveDraft(ParsedStructure(passwordValue = "pw")))31        assertNull(32            autofillSaveDraft(33                ParsedStructure(34                    webDomain = "example.com",35                    passwordValue = "pw",36                    credentialReleaseAllowed = false,37                ),38            ),39        )40    }4142    @Test43    fun planAutofillSave_addsNewWebLogin() {44        val draft = autofillSaveDraft(45            ParsedStructure(46                emailValue = "[email protected]",47                passwordValue = "new-secret",48                webDomain = "login.example.com",49            ),50        )!!5152        val plan = planAutofillSave(emptyList(), draft)5354        assertTrue(plan is AutofillSavePlan.Add)55        val entry = (plan as AutofillSavePlan.Add).entry56        assertEquals("login.example.com", entry.title)57        assertEquals("[email protected]", entry.email)58        assertEquals("new-secret", entry.password)59        assertEquals("https://login.example.com", entry.url)60    }6162    @Test63    fun planAutofillSave_updatesMatchingWebOriginAndIdentity() {64        val existing = entry("Example", "https://example.com/login", username = "june")65        val draft = AutofillSaveDraft(66            origin = "app.example.com",67            isWebOrigin = true,68            identity = "june",69            identityIsEmail = false,70            password = "new",71        )7273        val plan = planAutofillSave(listOf(existing), draft)7475        assertTrue(plan is AutofillSavePlan.Update)76        val updated = (plan as AutofillSavePlan.Update).entry77        assertEquals(existing.id, updated.id)78        assertEquals("new", updated.password)79        assertEquals("june", updated.username)80    }8182    @Test83    fun planAutofillSave_doesNotUpdateSubstringLookalikeOrigin() {84        val existing = entry("GitHub", "https://github.com/login", username = "june")85        val draft = AutofillSaveDraft(86            origin = "notgithub.com",87            isWebOrigin = true,88            identity = "june",89            identityIsEmail = false,90            password = "phished",91        )9293        val plan = planAutofillSave(listOf(existing), draft)9495        assertTrue(plan is AutofillSavePlan.Add)96    }9798    @Test99    fun planAutofillSave_updatesNativePackageOnlyOnExactOrigin() {100        val existing = entry("Native", "com.example.app", username = "june").copy(totp = "JBSWY3DPEHPK3PXP")101        val draft = AutofillSaveDraft(102            origin = "com.example.app",103            isWebOrigin = false,104            identity = "june",105            identityIsEmail = false,106            password = "new-native",107        )108109        val plan = planAutofillSave(listOf(existing), draft)110111        assertTrue(plan is AutofillSavePlan.Update)112        val updated = (plan as AutofillSavePlan.Update).entry113        assertEquals("new-native", updated.password)114        assertEquals("a save must never overwrite the stored seed", "JBSWY3DPEHPK3PXP", updated.totp)115    }116117    @Test118    fun autofillSaveDraft_ignores_a_live_otp_code_from_the_page() {119        // The page's OTP box holds whatever was typed into it, usually a live120        // six-digit code. Treating that as a seed silently destroyed the entry's121        // second factor (PB-26-135).122        val draft = autofillSaveDraft(123            ParsedStructure(124                webDomain = "example.com",125                passwordValue = "new",126                otpValue = "123456",127            ),128        )129130        assertTrue(draft != null)131        val existing = entry("Example", "https://example.com/login").copy(totp = "JBSWY3DPEHPK3PXP")132        val plan = planAutofillSave(listOf(existing), draft!!)133        assertEquals(134            "the stored seed is untouched by a save",135            "JBSWY3DPEHPK3PXP",136            (plan as AutofillSavePlan.Update).entry.totp,137        )138    }139}