6 Commits
Author SHA1 Message Date
clawbot 0aa9a52497 test: add test for getLongTermPrivateKey derivation index
Verifies that getLongTermPrivateKey reads the derivation index from
vault metadata instead of using hardcoded index 0. Test creates a
mock vault with DerivationIndex=5 and confirms the derived key
matches index 5.
2026-02-19 23:43:13 -08:00
clawbot 09ec79c57e fix: use vault derivation index in getLongTermPrivateKey instead of hardcoded 0
Previously, getLongTermPrivateKey() always used derivation index 0 when
deriving the long-term key from a mnemonic. This caused wrong key
derivation for vaults with index > 0 (second+ vault from same mnemonic),
leading to silent data corruption in keychain unlocker creation.

Now reads the vault's actual DerivationIndex from vault-metadata.json.
2026-02-19 23:43:13 -08:00
clawbot 4419ef7730 fix: non-darwin KeychainUnlocker stub returns errors instead of panicking
The stub previously panicked on all methods including NewKeychainUnlocker,
which is called from vault code when processing keychain-type unlocker
metadata. This caused crashes on Linux/Windows when a vault synced from
macOS contained keychain unlockers.

Now returns proper error values, allowing graceful degradation and
cross-platform vault portability.
2026-02-08 12:05:38 -08:00
clawbot 991b1a5a0b fix: remove redundant longterm.age encryption in Init command
CreatePassphraseUnlocker already encrypts and writes the long-term
private key to longterm.age. The Init command was doing this a second
time, overwriting the file with a functionally equivalent but
separately encrypted blob. This was wasteful and a maintenance hazard.
2026-02-08 12:05:09 -08:00
clawbot fd77a047f9 security: zero plaintext after copying to memguard in DecryptWithIdentity
The decrypted data from io.ReadAll was copied into a memguard
LockedBuffer but the original byte slice was never zeroed, leaving
plaintext in swappable, dumpable heap memory.
2026-02-08 12:04:38 -08:00
clawbot 341428d9ca fix: NumSecrets() now correctly counts secrets by checking for current file
NumSecrets() previously looked for non-directory, non-'current' files
directly under each secret directory, but the only children are
'current' (file, excluded) and 'versions' (directory, excluded),
so it always returned 0.

Now checks for the existence of the 'current' file, which is the
canonical indicator that a secret exists and has an active version.

This fixes the safety check in UnlockersRemove that was always
allowing removal of the last unlocker.
2026-02-08 12:04:15 -08:00