TrustedBrowsersTest.kt

6.5 KB · raw

1package com.peachpasswords.autofill23import org.junit.Assert.assertFalse4import org.junit.Assert.assertTrue5import org.junit.Test67/**8 * Locks the signer-set semantics for TrustedBrowsers.certificateSetMatches and9 * the fail-closed registry posture. These are pure JVM tests — no device or10 * emulator required.11 */12class TrustedBrowsersTest {1314    // certificateSetMatches: current-match accept1516    @Test fun current_match_accepts_on_api28() {17        assertTrue(18            TrustedBrowsers.certificateSetMatches(19                expected = setOf("aa", "bb"),20                current = setOf("AA"),21                history = emptySet(),22                apiLevel = 28,23            ),24        )25    }2627    @Test fun current_match_accepts_on_api26() {28        assertTrue(29            TrustedBrowsers.certificateSetMatches(30                expected = setOf("aa"),31                current = setOf("AA"),32                history = emptySet(),33                apiLevel = 26,34            ),35        )36    }3738    // certificateSetMatches: history-only must NOT accept (API 28+)3940    @Test fun history_only_rejects_on_api28() {41        // A package whose current signer is absent/unpinned but whose rotation42        // history contains a pinned cert must be rejected. History alone43        // cannot prove the current binary is authentic.44        assertFalse(45            "history-only match must not accept on API 28+",46            TrustedBrowsers.certificateSetMatches(47                expected = setOf("aa"),48                current = setOf("cc"),49                history = setOf("AA"),50                apiLevel = 28,51            ),52        )53    }5455    @Test fun history_only_with_empty_current_rejects_on_api28() {56        assertFalse(57            TrustedBrowsers.certificateSetMatches(58                expected = setOf("aa"),59                current = emptySet(),60                history = setOf("AA"),61                apiLevel = 28,62            ),63        )64    }6566    // certificateSetMatches: no-match reject6768    @Test fun no_match_rejects() {69        assertFalse(70            TrustedBrowsers.certificateSetMatches(71                expected = setOf("aa"),72                current = setOf("bb"),73                history = emptySet(),74                apiLevel = 28,75            ),76        )77    }7879    // certificateSetMatches: empty expected reject8081    @Test fun empty_expected_rejects() {82        assertFalse(83            TrustedBrowsers.certificateSetMatches(84                expected = emptySet(),85                current = setOf("aa"),86                history = emptySet(),87                apiLevel = 28,88            ),89        )90    }9192    // certificateSetMatches: empty current reject9394    @Test fun empty_current_rejects_on_api28() {95        assertFalse(96            TrustedBrowsers.certificateSetMatches(97                expected = setOf("aa"),98                current = emptySet(),99                history = emptySet(),100                apiLevel = 28,101            ),102        )103    }104105    @Test fun empty_current_rejects_on_api26() {106        assertFalse(107            TrustedBrowsers.certificateSetMatches(108                expected = setOf("aa"),109                current = emptySet(),110                history = emptySet(),111                apiLevel = 26,112            ),113        )114    }115116    // certificateSetMatches: API 26/27 vs 28+ divergence117    // On API 26/27, only current signers exist (no history parameter is used).118    // On API 28+, history is available but must NOT be used for acceptance.119120    @Test fun api26_ignores_history_parameter() {121        // Even if history is non-empty on API 26, only current matters.122        // (In practice, history is always empty on API 26/27.)123        assertFalse(124            TrustedBrowsers.certificateSetMatches(125                expected = setOf("aa"),126                current = setOf("bb"),127                history = setOf("AA"),128                apiLevel = 26,129            ),130        )131    }132133    @Test fun api27_current_match_accepts() {134        assertTrue(135            TrustedBrowsers.certificateSetMatches(136                expected = setOf("aa"),137                current = setOf("AA"),138                history = emptySet(),139                apiLevel = 27,140            ),141        )142    }143144    // Registry provenance: all pins must be UNCONFIRMED145146    @Test fun all_registry_pins_are_unconfirmed() {147        assertTrue(148            "every registry entry must be UNCONFIRMED until provenance is recorded",149            TrustedBrowsers.allPinsUnconfirmed(),150        )151    }152153    @Test fun registered_packages_include_expected_browsers() {154        val packages = TrustedBrowsers.registeredPackages()155        assertTrue(packages.contains("com.android.chrome"))156        assertTrue(packages.contains("org.mozilla.firefox"))157        assertTrue(packages.contains("com.brave.browser"))158    }159160    // isTrusted returns false for every pinned package while unconfirmed161162    @Test fun no_package_is_trusted_while_all_pins_unconfirmed() {163        // hasLocallyConfirmedPins must return false for every registered package.164        for (pkg in TrustedBrowsers.registeredPackages()) {165            assertFalse(166                "$pkg must not be trusted while its pin is UNCONFIRMED",167                TrustedBrowsers.hasLocallyConfirmedPins(pkg),168            )169        }170    }171172    // hasExpectedCertificate: positive and negative cases173174    @Test fun hasExpectedCertificate_positive_for_chrome() {175        assertTrue(176            TrustedBrowsers.hasExpectedCertificate(177                "com.android.chrome",178                "f0fd6c5b410f25cb25c3b53346c8972fae30f8ee7411df910480ad6b2d60db83",179            ),180        )181    }182183    @Test fun hasExpectedCertificate_negative_for_unknown_package() {184        assertFalse(185            TrustedBrowsers.hasExpectedCertificate(186                "com.evil.browser",187                "f0fd6c5b410f25cb25c3b53346c8972fae30f8ee7411df910480ad6b2d60db83",188            ),189        )190    }191192    @Test fun hasExpectedCertificate_negative_for_wrong_hash() {193        assertFalse(194            TrustedBrowsers.hasExpectedCertificate(195                "com.android.chrome",196                "0000000000000000000000000000000000000000000000000000000000000000",197            ),198        )199    }200201    // Case-insensitive matching202203    @Test fun certificateSetMatches_is_case_insensitive() {204        assertTrue(205            TrustedBrowsers.certificateSetMatches(206                expected = setOf("AABBCC"),207                current = setOf("aabbcc"),208                history = emptySet(),209                apiLevel = 28,210            ),211        )212    }213}