cc9b66d963a4
fix the second, independent bug blocking Codex restore: a malformed regex in VaultRepository.restoreFromCodex - the pattern's trailing wrapper-object close-brace was unescaped (\s*}\s*$ instead of \s*\}\s*$), which Android's on-device regex compiler rejects as a syntax error while the host JVM's regex engine silently tolerates it. This meant restoreFromCodex has likely never worked on a real device regardless of decode success - confirmed via a temporary debug log showing UnlockResult.Error with the exact 'Syntax error in regexp pattern' exception, and confirmed the existing VaultRepositoryExtraTest covering this path passes on the host JVM (explaining why it was never caught) despite failing identically every time on-device. Escaped the brace. Full end-to-end live verification: generated a fresh Codex PDF from a 5-entry vault (Pro-gated feature, activated with the real license key), saved it, navigated to Restore from Codex, imported the exact same file - 76/76 codes decoded (building on the fork's earlier vector-rendering fix), entered the master password, tapped restore, relaunched the app fresh, unlocked, and confirmed all 5 original entries present by name (Recovery Notes, Fastmail, Old Bank, Google with TOTP, GitHub). Codex generate+restore is now fully confirmed working, closing out the last unverified vault import/export format.
- time
- 2026-07-24 15:49 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- critical-corrections-c1-c7
- parent
- c6e41d31ec0a
↓ peach-android-cc9b66d9.tar.gzatlas at this save
files changed1 file
app/src/main/java/com/peachpasswords/vault/VaultRepository.kt+7 −1app/src/main/java/com/peachpasswords/vault/VaultRepository.kt+7 −1
@@ -97,7 +97,13 @@9797 suspend fun restoreFromCodex(vaultBlobBase64: String, password: String): UnlockResult = mutex.withLock {9898 return try {9999 val wrapperJson = String(Base64.getUrlDecoder().decode(vaultBlobBase64), Charsets.UTF_8)100- val slotJson = Regex("\"a\"\\s*:\\s*(\\{.*\\})\\s*}\\s*$").find(wrapperJson)?.groupValues?.get(1)100+ // The trailing `}` (the wrapper object's own close-brace, after the101+ // captured inner slot object) must be escaped — Android's regex102+ // compiler rejects a bare, unescaped `}` outside a quantifier103+ // context as a syntax error, so this never matched anything and104+ // Codex restore always failed with a generic "invalid_codex_blob"105+ // regardless of whether the codes themselves decoded correctly.106+ val slotJson = Regex("\"a\"\\s*:\\s*(\\{.*\\})\\s*\\}\\s*$").find(wrapperJson)?.groupValues?.get(1)101107 ?: return UnlockResult.Error("invalid_codex_blob")102108 val slotA = VaultCodec.decodeSlot(slotJson) ?: return UnlockResult.Error("invalid_codex_blob")103109 if (!store.writeSlotA(slotA)) return UnlockResult.Error("storage_write_failed")