Vault 'Change PIN' uses window.prompt() — broken on Capacitor Android/iOS build #189
Labels
No labels
blocked:depends-p1
blocked:gate-research
blocked:lint-failed
blocked:needs-decision
blocked:needs-human
blocked:verify-failed
bug
data-loss
documentation
enhancement
feature-request
in-progress
needs-decompose
needs-triage
pwa
quickfix
security
security/high
security/medium
test
test-coverage
triage-approved
vault
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/kimi_agent_kin_app_sync#189
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
In
Settings -> Vault -> Change PIN, clicking the Change PIN button invokeswindow.prompt("Enter new 4-digit PIN:")to collect the new PIN.On the Vite dev server (Chromium), this works as a synchronous blocking dialog. On the Capacitor Android/iOS build (which is the production target -- see
package.json: @capacitor/android,@capacitor/ios),window.promptis not implemented in the WebView. The click handler throws immediately, the user can never change their PIN from Settings, and the existing PIN remains the only credential. There is no error path -- the throw is silent.Repro (Android WebView)
Settings -> Vault -> Change PIN.Repro (dev -- confirms handler is wired to
prompt)Driving the dev server via CDP and clicking the button will hang waiting for prompt input because the CDP driver has no protocol for
window.prompt. InspectingSettings.tsx:1093shows the source.Source pointer
src/pages/Settings.tsx:1088-1107Comparison
The Vault enrollment flow on
#/vault(src/pages/Vault.tsx) correctly uses real React inputs inside a multi-step wizard (choose factor -> enter PIN -> confirm PIN -> enroll). The Settings "Change PIN" should follow the same pattern: a Radix Dialog with<input type=password inputMode=numeric maxLength=4>.Severity
Functional. Affects all production builds (Capacitor Android + iOS). Dev server is unaffected.
Suggested fix
Replace
prompt(...)with a controlled dialog state and a Radix<Dialog>containing:<input type="password" inputMode="numeric" maxLength={4} value={pin} onChange={...} />changeVaultPin(pin)if/^\d{4}$/.test(pin), elsetoast.error("PIN must be exactly 4 digits")The
changeVaultPinaction insrc/lib/store.ts:1351is correctly implemented -- only the UI binding is broken.QA context
Verified during the 2026-09-20 privacy vault + feature tracking shakedown. All other vault/privacy switches (Intimacy Logging, Vault Enabled, Relational Cadence Tracking, On-Device-Only, Investment Tracking Paused, Auto-lock Delay, Panic Decoy, Shake Threshold) persist correctly across hard navigation. Vault PIN enrollment, unlock, wrong-PIN rejection, lock-now, and panic gesture redirect-to-decoy all work as expected.