3f8929c5bbd2
fix: resolve assessment PEACH-AND-REPORT-001 open items, gate green (v2.10.20)
- time
- 2026-08-04 04:33 UTC · 2 months ago
- machine
- koh2
- parent
- e3cf8dbbbba2
↓ peach-android-3f8929c5.tar.gzatlas at this save
files changed7 files
VERSION+1 −1app/src/main/java/com/peachpasswords/passkeys/PasskeyCallerOrigin.kt+5 −0app/src/main/java/com/peachpasswords/ui/vault/EntryDetailScreen.kt+3 −0app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+3 −0app/src/test/java/com/peachpasswords/security/SecureFlagCoverageTest.kt+3 −2app/src/test/java/com/peachpasswords/sync/SyncOutcomeTest.kt+4 −3app/src/test/java/com/peachpasswords/ui/settings/SettingsStackTest.kt+6 −6app/src/main/java/com/peachpasswords/passkeys/PasskeyCallerOrigin.kt+5 −0
@@ -1,6 +1,7 @@11 package com.peachpasswords.passkeys22 33 import android.content.Context4+import android.os.Build45 import androidx.credentials.provider.CallingAppInfo56 import com.peachpasswords.R67 import com.peachpasswords.shared.extractDomain@@ -49,6 +50,10 @@4950 fun resolve(callingAppInfo: CallingAppInfo, privilegedAllowlist: String): String? {5051 val webOrigin = runCatching { callingAppInfo.getOrigin(privilegedAllowlist) }.getOrNull()5152 if (!webOrigin.isNullOrBlank()) return webOrigin53+ // SigningInfo.getApkContentsSigners requires API 28 (minSdk 26). Passkey54+ // callers only exist on 34+, but fail closed — never crash — if this is55+ // ever reached on an older platform.56+ if (Build.VERSION.SDK_INT < 28) return null5257 val signer = runCatching { callingAppInfo.signingInfo.apkContentsSigners.firstOrNull() }.getOrNull()5358 return signer?.let { apkKeyHashOrigin(it.toByteArray()) }5459 }app/src/main/java/com/peachpasswords/ui/vault/EntryDetailScreen.kt+3 −0
@@ -83,6 +83,7 @@8383 import com.peachpasswords.ui.components.PeachEmptyOrEndState8484 import com.peachpasswords.ui.components.PeachMark8585 import com.peachpasswords.ui.components.PeachTopBar86+import com.peachpasswords.ui.components.SecureFlagEffect8687 import com.peachpasswords.ui.components.TOTPRing8788 import com.peachpasswords.ui.lock.Biometrics8889 import com.peachpasswords.ui.theme.LocalPeachAccent@@ -110,6 +111,8 @@110111 modifier: Modifier = Modifier,111112 vm: EntryDetailViewModel = hiltViewModel(),112113 ) {114+ SecureFlagEffect()115+113116 val entry by vm.entry.collectAsState()114117 val activeEntries by vm.activeEntries.collectAsState()115118 val identitySuggestions by vm.identitySuggestions.collectAsState()app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+3 −0
@@ -47,6 +47,7 @@4747 import com.peachpasswords.ui.components.PeachEmptyOrEndState4848 import com.peachpasswords.ui.components.PeachSegment4949 import com.peachpasswords.ui.components.PeachSegmentedControl50+import com.peachpasswords.ui.components.SecureFlagEffect5051 import com.peachpasswords.ui.components.PeachTopBar5152 import com.peachpasswords.ui.theme.LocalPeachAccent5253 import com.peachpasswords.ui.theme.PeachMotion@@ -63,6 +64,8 @@6364 onOpenNote: (String) -> Unit = {},6465 vm: EntryEditViewModel = hiltViewModel(),6566 ) {67+ SecureFlagEffect()68+6669 val f by vm.form.collectAsState()6770 val usernameSuggestions by vm.usernameSuggestions.collectAsState()6871 val emailSuggestions by vm.emailSuggestions.collectAsState()app/src/test/java/com/peachpasswords/security/SecureFlagCoverageTest.kt+3 −2
@@ -75,9 +75,10 @@7575 "../app/src/main/java/com/peachpasswords/ui/components/SecureWindowPolicy.kt",7676 )7777 val source = helper.readText()78- assertTrue(source.contains("installRelease"))78+ assertTrue(source.contains("setUserSecure"))79+ assertTrue(source.contains("acquire"))80+ assertTrue(source.contains("release"))7981 assertTrue(source.contains("leases"))80- assertTrue(source.contains("releaseOwned"))8182 assertTrue(source.contains("clearFlags"))8283 // MainActivity and secret activities delegate to the owner; they must8384 // not carry a second ad-hoc clear/set lifecycle that can drift.app/src/test/java/com/peachpasswords/sync/SyncOutcomeTest.kt+4 −3
@@ -38,14 +38,14 @@3838 * Network-class outcomes (relay/handshake/timeout/connection_lost) live in3939 * [SyncOutcomeReason.isWarning] and resolve to the amber light, not red —4040 * the background loop keeps trying, so a transient miss is a warning, not41- * a problem.41+ * a problem. VAULT_LOCKED joined them in v2.9.35 for the same reason: the42+ * loop retries once the vault unlocks, so amber, not red.4243 */4344 @Test fun `the failing outcomes are exactly these`() {4445 assertEquals(4546 setOf(4647 SyncOutcomeReason.NO_ELIGIBLE_HOST,4748 SyncOutcomeReason.PRIVATE_RELAY_UNSET,48- SyncOutcomeReason.VAULT_LOCKED,4949 // Android refused to start the runner: nothing synced, and the5050 // person can do something about it.5151 SyncOutcomeReason.FOREGROUND_START_BLOCKED,@@ -56,9 +56,10 @@5656 )5757 }5858 59- @Test fun `the warning outcomes are exactly the network class`() {59+ @Test fun `the warning outcomes are the network class plus a locked vault`() {6060 assertEquals(6161 setOf(62+ SyncOutcomeReason.VAULT_LOCKED,6263 SyncOutcomeReason.RELAY_UNREACHABLE,6364 SyncOutcomeReason.HANDSHAKE_FAILED,6465 SyncOutcomeReason.SYNC_TIMEOUT,app/src/test/java/com/peachpasswords/ui/settings/SettingsStackTest.kt+6 −6
@@ -27,15 +27,15 @@2727 2828 @Test fun settings_deck_fills_down_to_just_above_the_build_label() {2929 // Reserves the label's nav-bar inset (passed in) + its padding/line height30- // (xs 4dp + 12dp = 16dp) + a 6dp gap = navBarBottom + 22dp. No hard cap, so30+ // (xs 4dp + 12dp = 16dp) + a 1dp gap = navBarBottom + 17dp. No hard cap, so3131 // tall panels (Galaxy Z Fold 7) fill to the label instead of stranding dead3232 // space below the stack and clipping the Pro card's copy/reveal buttons.33- assertEquals(878.dp, settingsDeckHeight(900.dp, 0.dp))34- assertEquals(458.dp, settingsDeckHeight(480.dp, 0.dp))35- assertEquals(2.dp, settingsDeckHeight(24.dp, 0.dp))36- assertEquals(1178.dp, settingsDeckHeight(1200.dp, 0.dp))33+ assertEquals(883.dp, settingsDeckHeight(900.dp, 0.dp))34+ assertEquals(463.dp, settingsDeckHeight(480.dp, 0.dp))35+ assertEquals(7.dp, settingsDeckHeight(24.dp, 0.dp))36+ assertEquals(1183.dp, settingsDeckHeight(1200.dp, 0.dp))3737 // A larger nav-bar inset (three-button nav) simply reserves more at the bottom.38- assertEquals(830.dp, settingsDeckHeight(900.dp, 48.dp))38+ assertEquals(835.dp, settingsDeckHeight(900.dp, 48.dp))3939 }4040 4141 // ── Drag gesture resolution ───────────────────────────────────────────────