# Implementation Plan: 2026-09-01 Security Assessment Remediation ## Overview Resolve every open finding in `docs/peach-android-independent-security-assessment-2026-09-01.pdf` against the current v2.14.89 tree. Work is ordered by release risk, delivered as small Koh saves, and kept separate from the deferred PIN hardware-validation queue. ## Architecture Decisions - Treat `local.properties` as SDK configuration only. Release signing secrets must arrive through protected environment injection, and release builds fail closed when the required inputs are absent. - Permit external URL dispatch only for canonical HTTPS URLs. Bare hosts normalize to HTTPS; HTTP and all non-web schemes are rejected. - Authenticate update metadata before trusting any manifest field, using a pinned offline verification key and a versioned signed envelope. Existing APK checksum, identity, version, and signer-lineage gates remain. - Keep release logging to opaque, redacted event identifiers. Debug builds may retain diagnostic detail. - Preserve favicon opt-in but require informed consent naming the provider and disclosure scope; validate domains and cache containment inside the favicon helper itself. - Pin the Rust toolchain and verify the native build provenance without introducing a new dependency. ## Task List ### Phase 1: Release-root blockers - [ ] PB-26-025: remove plaintext signing secrets and investigate exposure/rotation. - [ ] PB-26-026: enable and validate R8 plus resource shrinking. - [ ] PB-26-027: restrict external URL opening to canonical HTTPS. ### Checkpoint: Release blockers - [ ] Fresh-workspace release signing uses protected injection only. - [ ] Shrunk release builds and critical device flows pass. - [ ] Hostile and non-HTTPS URI tests pass. ### Phase 2: Integrity and privacy boundaries - [ ] PB-26-028: require independently signed update metadata. - [ ] PB-26-029: remove debug credential literals from production source/package. - [ ] PB-26-030: add informed favicon consent and opt-out network proof. - [ ] PB-26-031: enforce redacted release logging. ### Checkpoint: Integrity and privacy - [ ] Forged, stale, rollback, alternate-URL, and key-rotation update cases are covered. - [ ] Release APK contains no demo credential literals. - [ ] Opt-out produces no favicon requests; opt-in copy names DuckDuckGo and inventory disclosure. - [ ] Malformed sync/pairing inputs emit only approved opaque release events. ### Phase 3: Supply-chain and helper hardening - [ ] PB-26-032: pin Rust and native build provenance. - [ ] PB-26-033: validate favicon hosts and canonicalize cache paths at the helper boundary. ### Checkpoint: Complete - [ ] Full JVM tests, lint, release build, and applicable instrumentation/smoke gates pass. - [ ] Release artifacts are inspected for shrinking, credential literals, signer identity, and native provenance. - [ ] Every finding has a resolution record, Koh save, validation evidence, and residual-risk statement. - [ ] The original PIN hardware-validation queue remains unchanged for follow-up. ## Finding Resolution Details ### PB-26-025 - Signing credentials **Acceptance criteria:** no signing password or keystore path is read from `local.properties`; local-only configuration is ignored; missing protected inputs cannot silently produce a publishable release; a sanitized history/workspace search is recorded. Rotation, revocation, backup/CI review, and old-signer disposition are owner actions and must not be falsely marked complete from source changes alone. **Verification:** configuration tests or Gradle task checks cover missing/present injection; `koh diff` contains no secret value; a fresh-workspace release build path is documented and exercised where credentials are available. **Likely files:** `.gitignore` or Koh ignore configuration, `app/build.gradle`, release documentation. ### PB-26-026 - Release shrinking **Acceptance criteria:** release enables `minifyEnabled` and `shrinkResources`; narrow project keep rules exist; security-sensitive reflection/serialization paths continue working. **Verification:** unit, lint, release, APK inspection, and device smoke tests for unlock, autofill, passkeys, updates, crypto/import, and sync/pairing. **Likely files:** `app/build.gradle`, `app/proguard-rules.pro`, focused tests/documentation. ### PB-26-027 - External URL allowlist **Acceptance criteria:** bare valid hosts and HTTPS normalize safely; HTTP, `intent:`, `file:`, custom schemes, malformed authorities, credentials, control characters, and encoded separator attacks do not reach ACTION_VIEW. **Verification:** focused pure unit tests first, then compile and a manual open-link smoke test. **Likely files:** `AdditionalUrlsDetail.kt`, a focused JVM test. ### PB-26-028 - Signed update metadata **Acceptance criteria:** a versioned signature covers the exact trusted manifest bytes; verification uses a pinned public key before parsing URLs/checksums/notes; unknown keys, invalid signatures, stale metadata, and rollback fail closed. **Verification:** deterministic signature vectors plus forged HTTPS, key transition, alternate URL, stale, and rollback tests. **Dependencies:** release-side offline signing key and manifest signing step must be available before completion. The current local architecture spans three repositories: Koh publishes the artifacts, Kepr persists and serves the release record, and Peach verifies it. Koh's updater already defines a per-platform Ed25519 envelope shape, but the build/offer path does not produce those signatures and Kepr's generic endpoint does not persist or emit them. Reuse that shape only after defining one canonical byte payload and a production key-custody boundary. The public Kepr service must not hold the offline private key. **Likely files:** `UpdateRepository.kt`, manifest model/tests, Kepr build/release scripts and documentation. ### PB-26-029 - Debug seed isolation **Acceptance criteria:** predictable usernames, passwords, and TOTP seeds leave production source; debug QA data is generated at runtime or isolated under debug/test sources; release artifact string/symbol inspection is clean. **Verification:** debug seed behavior test plus release APK scan. **Likely files:** `VaultListViewModel.kt`, debug source-set helper, focused tests. ### PB-26-030 - Favicon consent **Acceptance criteria:** enabling site icons requires explicit copy naming DuckDuckGo and explaining one request per distinct vault domain; declining or disabling causes no network requests; prior opt-ins are re-consented after the copy change. **Verification:** settings-state tests, UI test where practical, and network-capture/fake-client proof of zero opt-out requests. **Likely files:** settings model/UI, `VaultListScreen.kt`, favicon tests and privacy documentation. ### PB-26-031 - Release logging **Acceptance criteria:** production sync/pairing logs contain only allowlisted opaque event names and redacted error classes; payloads, peer identifiers, protocol material, raw exception messages, and stack traces are excluded. **Verification:** logger unit tests and a release log capture under malformed inputs. **Likely files:** sync logging helper and the three reported sync/adoption call sites. ### PB-26-032 - Rust provenance **Acceptance criteria:** Rust channel/version and target inputs are pinned; Cargo uses the lockfile; produced native artifacts have recorded source/toolchain/target hashes or equivalent reproducible provenance. **Verification:** pinned-toolchain build and provenance output comparison. **Likely files:** `rust-toolchain.toml`, root `build.gradle`, native build/release documentation. ### PB-26-033 - Favicon cache containment **Acceptance criteria:** helper accepts only canonical validated DNS hosts; every hit/miss cache path remains a direct child of the favicon cache directory; invalid caller material cannot create, read, or write another path. **Verification:** traversal, separator, Unicode/control, malformed-host, and valid-IDN/domain unit tests. **Likely files:** `Favicons.kt`, focused tests. ## Risks and Mitigations | Risk | Impact | Mitigation | |---|---|---| | Signing credentials may already exist outside this workspace | Source cleanup cannot revoke compromise | Keep release blocked until the owner completes rotation/exposure review | | R8 removes reflected or JNI-reached code | Runtime-only regression | Start with narrow rules, inspect mapping/usage, and run device smoke gates | | Signed metadata requires release-channel coordination | Client-only change could block all updates | Land signer tooling and client verification as one coordinated, rollback-tested unit | | Re-consent reduces favicon adoption | Expected privacy tradeoff | Use concise, explicit copy and preserve initials fallback | | Log reduction hurts diagnostics | Production failures become harder to investigate | Retain opaque event IDs and detailed debug-only logs | ## Open Questions - The release-signing values' exposure beyond this machine, rotation capability, and old-signer disposition are UNCONFIRMED. - The offline update-metadata signing key custodian and Kepr manifest-signing integration are UNCONFIRMED. - Physical-device availability for the R8 and release-log smoke matrices is UNCONFIRMED.