package com.peachpasswords.autofill import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test /** * Locks the signer-set semantics for TrustedBrowsers.certificateSetMatches and * the fail-closed registry posture. These are pure JVM tests — no device or * emulator required. */ class TrustedBrowsersTest { // certificateSetMatches: current-match accept @Test fun current_match_accepts_on_api28() { assertTrue( TrustedBrowsers.certificateSetMatches( expected = setOf("aa", "bb"), current = setOf("AA"), history = emptySet(), apiLevel = 28, ), ) } @Test fun current_match_accepts_on_api26() { assertTrue( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = setOf("AA"), history = emptySet(), apiLevel = 26, ), ) } // certificateSetMatches: history-only must NOT accept (API 28+) @Test fun history_only_rejects_on_api28() { // A package whose current signer is absent/unpinned but whose rotation // history contains a pinned cert must be rejected. History alone // cannot prove the current binary is authentic. assertFalse( "history-only match must not accept on API 28+", TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = setOf("cc"), history = setOf("AA"), apiLevel = 28, ), ) } @Test fun history_only_with_empty_current_rejects_on_api28() { assertFalse( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = emptySet(), history = setOf("AA"), apiLevel = 28, ), ) } // certificateSetMatches: no-match reject @Test fun no_match_rejects() { assertFalse( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = setOf("bb"), history = emptySet(), apiLevel = 28, ), ) } // certificateSetMatches: empty expected reject @Test fun empty_expected_rejects() { assertFalse( TrustedBrowsers.certificateSetMatches( expected = emptySet(), current = setOf("aa"), history = emptySet(), apiLevel = 28, ), ) } // certificateSetMatches: empty current reject @Test fun empty_current_rejects_on_api28() { assertFalse( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = emptySet(), history = emptySet(), apiLevel = 28, ), ) } @Test fun empty_current_rejects_on_api26() { assertFalse( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = emptySet(), history = emptySet(), apiLevel = 26, ), ) } // certificateSetMatches: API 26/27 vs 28+ divergence // On API 26/27, only current signers exist (no history parameter is used). // On API 28+, history is available but must NOT be used for acceptance. @Test fun api26_ignores_history_parameter() { // Even if history is non-empty on API 26, only current matters. // (In practice, history is always empty on API 26/27.) assertFalse( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = setOf("bb"), history = setOf("AA"), apiLevel = 26, ), ) } @Test fun api27_current_match_accepts() { assertTrue( TrustedBrowsers.certificateSetMatches( expected = setOf("aa"), current = setOf("AA"), history = emptySet(), apiLevel = 27, ), ) } // Registry provenance: all pins must be UNCONFIRMED @Test fun all_registry_pins_are_unconfirmed() { assertTrue( "every registry entry must be UNCONFIRMED until provenance is recorded", TrustedBrowsers.allPinsUnconfirmed(), ) } @Test fun registered_packages_include_expected_browsers() { val packages = TrustedBrowsers.registeredPackages() assertTrue(packages.contains("com.android.chrome")) assertTrue(packages.contains("org.mozilla.firefox")) assertTrue(packages.contains("com.brave.browser")) } // isTrusted returns false for every pinned package while unconfirmed @Test fun no_package_is_trusted_while_all_pins_unconfirmed() { // hasLocallyConfirmedPins must return false for every registered package. for (pkg in TrustedBrowsers.registeredPackages()) { assertFalse( "$pkg must not be trusted while its pin is UNCONFIRMED", TrustedBrowsers.hasLocallyConfirmedPins(pkg), ) } } // hasExpectedCertificate: positive and negative cases @Test fun hasExpectedCertificate_positive_for_chrome() { assertTrue( TrustedBrowsers.hasExpectedCertificate( "com.android.chrome", "f0fd6c5b410f25cb25c3b53346c8972fae30f8ee7411df910480ad6b2d60db83", ), ) } @Test fun hasExpectedCertificate_negative_for_unknown_package() { assertFalse( TrustedBrowsers.hasExpectedCertificate( "com.evil.browser", "f0fd6c5b410f25cb25c3b53346c8972fae30f8ee7411df910480ad6b2d60db83", ), ) } @Test fun hasExpectedCertificate_negative_for_wrong_hash() { assertFalse( TrustedBrowsers.hasExpectedCertificate( "com.android.chrome", "0000000000000000000000000000000000000000000000000000000000000000", ), ) } // Case-insensitive matching @Test fun certificateSetMatches_is_case_insensitive() { assertTrue( TrustedBrowsers.certificateSetMatches( expected = setOf("AABBCC"), current = setOf("aabbcc"), history = emptySet(), apiLevel = 28, ), ) } }