fix(security): app-lock lifecycle consistency (grace across Back, evict passphrase on re-lock) #101

Closed
opened 2026-07-02 02:55:55 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-07-02 02:55:55 +00:00 (Migrated from github.com)

Origin: code review of PR #45 (screen-lock app gate). Below-the-cut lifecycle-consistency items.

Problem

  • Grace period doesn't survive leaving via Back. AppLockViewModel/AppLockGate are scoped to MainActivity's ViewModelStore via hiltViewModel(). On API 29/30, Back on the root destination finishes the activity and clears the store, so re-entry 2 s later re-arms a fresh LOCKED gate and demands full re-auth — while leaving via Home and returning within 30 s stays unlocked. Behaves differently on API 31+ (root Back moves the task to back). Contradicts the documented 30 s grace rule the JVM tests pin.
  • PassphraseSession is never re-locked on timeout. Its KDoc promises the passphrase is "cleared on lock, timeout," but no code path calls session.lock() on gate re-lock/grace expiry — only onAuthError/RETRY call gate.lock(), never session.lock(). The UI gate is Activity-scoped while the secret is a process-scoped @Singleton plus an already-open Room handle, so a "locked" app still holds the raw passphrase in memory for the process lifetime. Doc/impl mismatch that a future "evict on timeout" feature will trip over (it also needs a DB close/reopen mechanism, since provideDatabase runs once per process).

Suggested fix

Hoist the grace/lock state so it survives activity recreation (application-scoped, or persist backgroundedAt), and either implement session eviction on re-lock (with the DB-handle lifecycle it requires) or correct the KDoc to state the accepted limitation explicitly. Relates to the two-tier gate/secret design.

Origin: code review of PR #45 (screen-lock app gate). Below-the-cut lifecycle-consistency items. ## Problem - **Grace period doesn't survive leaving via Back.** `AppLockViewModel`/`AppLockGate` are scoped to `MainActivity`'s ViewModelStore via `hiltViewModel()`. On API 29/30, Back on the root destination finishes the activity and clears the store, so re-entry 2 s later re-arms a fresh LOCKED gate and demands full re-auth — while leaving via Home and returning within 30 s stays unlocked. Behaves differently on API 31+ (root Back moves the task to back). Contradicts the documented 30 s grace rule the JVM tests pin. - **`PassphraseSession` is never re-locked on timeout.** Its KDoc promises the passphrase is "cleared on lock, timeout," but no code path calls `session.lock()` on gate re-lock/grace expiry — only `onAuthError`/RETRY call `gate.lock()`, never `session.lock()`. The UI gate is Activity-scoped while the secret is a process-scoped `@Singleton` plus an already-open Room handle, so a "locked" app still holds the raw passphrase in memory for the process lifetime. Doc/impl mismatch that a future "evict on timeout" feature will trip over (it also needs a DB close/reopen mechanism, since `provideDatabase` runs once per process). ## Suggested fix Hoist the grace/lock state so it survives activity recreation (application-scoped, or persist `backgroundedAt`), and either implement session eviction on re-lock (with the DB-handle lifecycle it requires) or correct the KDoc to state the accepted limitation explicitly. Relates to the two-tier gate/secret design.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#101