Removing the current unlocker leaves the vault unable to open through its remaining unlocker #137

Open
opened 2026-10-13 00:29:59 +02:00 by clawbot · 0 comments
Collaborator

Severity: major
After secret unlocker remove removes the vault's current unlocker, every command that opens the vault fails although another working unlocker remains, and the error sends the user to the mnemonic instead of to secret unlocker select. Nothing is lost; unlocker select <other-id> repairs it.

What happens: RemoveUnlocker deletes the unlocker's directory and leaves current-unlocker naming it. GetCurrentUnlocker then fails on the missing metadata file, and withMnemonicAdvice adds "the vault still opens with its mnemonic: run 'secret unlocker add passphrase' ...". That advice is wrong here: unlocker add passphrase without the mnemonic fails the same way, and the vault needs no new unlocker. secret vault remove of the current vault switches to another vault first; removing the current unlocker does nothing of the kind. The confirmation says only "It is not the vault's last unlocker."

Reproduction (a throwaway GPG key in a fresh GNUPGHOME):

SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init
echo -n hunter2 | SB_UNLOCK_PASSPHRASE=pw secret add foo
SB_UNLOCK_PASSPHRASE=pw secret unlocker add pgp --keyid "$FPR"   # becomes current
secret unlocker remove --force "$PGPID"                           # succeeds
SB_UNLOCK_PASSPHRASE=pw secret get foo
# Error: ... failed to get current unlocker: failed to read unlocker metadata: open .../unlockers.d/$PGPID/unlocker-metadata.json: no such file or directory; the vault 'default' still opens with its mnemonic: run 'secret unlocker add passphrase' ...
cat .../vaults.d/default/current-unlocker   # still $PGPID

Where:

func (v *Vault) RemoveUnlocker(unlockerID string) error {
(RemoveUnlocker),
func (cli *Instance) removeUnlocker(
(removeUnlocker),
func (v *Vault) withMnemonicAdvice(err error) error {
(withMnemonicAdvice).

Correct behaviour is a product decision the README does not settle. Recommended: when the removed unlocker is current and another unlocker remains, make that one current in the same command (as vault remove does for vaults) and say so; when none remains, clear current-unlocker. In any case the error for a current-unlocker naming a missing directory should name secret unlocker select when other unlockers exist.

Definition of done

  • Removing the current unlocker leaves the vault openable through a remaining unlocker without a further command, or the error after it names secret unlocker select.
  • A regression test removes the current of two unlockers and requires GetOrDeriveLongTermKey to succeed through the other; it fails before the fix.

Model: fable-5-1

Severity: major After `secret unlocker remove` removes the vault's current unlocker, every command that opens the vault fails although another working unlocker remains, and the error sends the user to the mnemonic instead of to `secret unlocker select`. Nothing is lost; `unlocker select <other-id>` repairs it. What happens: `RemoveUnlocker` deletes the unlocker's directory and leaves `current-unlocker` naming it. `GetCurrentUnlocker` then fails on the missing metadata file, and `withMnemonicAdvice` adds "the vault still opens with its mnemonic: run 'secret unlocker add passphrase' ...". That advice is wrong here: `unlocker add passphrase` without the mnemonic fails the same way, and the vault needs no new unlocker. `secret vault remove` of the current vault switches to another vault first; removing the current unlocker does nothing of the kind. The confirmation says only "It is not the vault's last unlocker." Reproduction (a throwaway GPG key in a fresh `GNUPGHOME`): ``` SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init echo -n hunter2 | SB_UNLOCK_PASSPHRASE=pw secret add foo SB_UNLOCK_PASSPHRASE=pw secret unlocker add pgp --keyid "$FPR" # becomes current secret unlocker remove --force "$PGPID" # succeeds SB_UNLOCK_PASSPHRASE=pw secret get foo # Error: ... failed to get current unlocker: failed to read unlocker metadata: open .../unlockers.d/$PGPID/unlocker-metadata.json: no such file or directory; the vault 'default' still opens with its mnemonic: run 'secret unlocker add passphrase' ... cat .../vaults.d/default/current-unlocker # still $PGPID ``` Where: https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/vault/unlockers.go#L299 (`RemoveUnlocker`), https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/cli/unlockers.go#L705 (`removeUnlocker`), https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/vault/vault.go#L309 (`withMnemonicAdvice`). Correct behaviour is a product decision the README does not settle. Recommended: when the removed unlocker is current and another unlocker remains, make that one current in the same command (as `vault remove` does for vaults) and say so; when none remains, clear `current-unlocker`. In any case the error for a `current-unlocker` naming a missing directory should name `secret unlocker select` when other unlockers exist. **Definition of done** - Removing the current unlocker leaves the vault openable through a remaining unlocker without a further command, or the error after it names `secret unlocker select`. - A regression test removes the current of two unlockers and requires `GetOrDeriveLongTermKey` to succeed through the other; it fails before the fix. Model: fable-5-1
sneak was assigned by clawbot 2026-10-13 00:29:59 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#137