Severity: major
A secret add --force that fails reports an error yet has changed the secret: it leaves a new version behind that version list shows, so a failed command is not a no-op and the version history gains an entry the user never saw succeed. Nothing is lost.
What happens: addVersion writes the new version directory first, then unlocks the vault to stamp notAfter on the previous version, then rewrites current. The unlock is the step that fails in normal use: a mistyped passphrase, or a wrong SB_SECRET_MNEMONIC. By then the version is already in versions/, named as the newest serial, with the new value encrypted in it; current still names the old version. A crash between the first and last step leaves the same state.
Reproduction:
SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init
echo -n v1 | SB_UNLOCK_PASSPHRASE=pw secret add foo
echo -n v2 | SB_UNLOCK_PASSPHRASE=wrong secret add --force foo
# Error: failed to get long-term key: ... no identity matched any of the recipients ...; exit 1
SB_UNLOCK_PASSPHRASE=pw secret version list foo
# 20261012.002 ... expired (the failed add's version, notAfter unset)
# 20261012.001 ... current
SB_UNLOCK_PASSPHRASE=pw secret get foo # v1
Every further failed add --force adds one more. generate secret --force and import --force take the same path.
(addVersion): createAndSaveVersion at L225 runs before updatePreviousVersion at L232, which is where GetOrDeriveLongTermKey first runs for an existing secret.
Correct behaviour: a failed add changes nothing. Get the long-term key (and load the previous version's metadata) before writing the new version, so a wrong passphrase or mnemonic fails before anything is written; only the rename of current can then still fail.
Definition of done
After add --force fails to unlock the vault, version list shows the same versions as before and the versions directory is unchanged.
A regression test records the secret directory, runs AddSecret with force and a wrong passphrase, and requires the directory unchanged; it fails before the fix.
Model: fable-5-1
Severity: major
A `secret add --force` that fails reports an error yet has changed the secret: it leaves a new version behind that `version list` shows, so a failed command is not a no-op and the version history gains an entry the user never saw succeed. Nothing is lost.
What happens: `addVersion` writes the new version directory first, then unlocks the vault to stamp `notAfter` on the previous version, then rewrites `current`. The unlock is the step that fails in normal use: a mistyped passphrase, or a wrong `SB_SECRET_MNEMONIC`. By then the version is already in `versions/`, named as the newest serial, with the new value encrypted in it; `current` still names the old version. A crash between the first and last step leaves the same state.
Reproduction:
```
SB_UNLOCK_PASSPHRASE=pw SB_SECRET_MNEMONIC="$M" secret init
echo -n v1 | SB_UNLOCK_PASSPHRASE=pw secret add foo
echo -n v2 | SB_UNLOCK_PASSPHRASE=wrong secret add --force foo
# Error: failed to get long-term key: ... no identity matched any of the recipients ...; exit 1
SB_UNLOCK_PASSPHRASE=pw secret version list foo
# 20261012.002 ... expired (the failed add's version, notAfter unset)
# 20261012.001 ... current
SB_UNLOCK_PASSPHRASE=pw secret get foo # v1
```
Every further failed `add --force` adds one more. `generate secret --force` and `import --force` take the same path.
Where: https://git.eeqj.de/sneak/secret/src/commit/ef0ae90768c8b18a829033b3ebd4cf98fcaeb3d1/internal/vault/secrets.go#L218 (`addVersion`): `createAndSaveVersion` at L225 runs before `updatePreviousVersion` at L232, which is where `GetOrDeriveLongTermKey` first runs for an existing secret.
Correct behaviour: a failed `add` changes nothing. Get the long-term key (and load the previous version's metadata) before writing the new version, so a wrong passphrase or mnemonic fails before anything is written; only the rename of `current` can then still fail.
**Definition of done**
- After `add --force` fails to unlock the vault, `version list` shows the same versions as before and the versions directory is unchanged.
- A regression test records the secret directory, runs `AddSecret` with `force` and a wrong passphrase, and requires the directory unchanged; it fails before the fix.
Model: fable-5-1
sneak
was assigned by clawbot2026-10-13 00:29:41 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: major
A
secret add --forcethat fails reports an error yet has changed the secret: it leaves a new version behind thatversion listshows, so a failed command is not a no-op and the version history gains an entry the user never saw succeed. Nothing is lost.What happens:
addVersionwrites the new version directory first, then unlocks the vault to stampnotAfteron the previous version, then rewritescurrent. The unlock is the step that fails in normal use: a mistyped passphrase, or a wrongSB_SECRET_MNEMONIC. By then the version is already inversions/, named as the newest serial, with the new value encrypted in it;currentstill names the old version. A crash between the first and last step leaves the same state.Reproduction:
Every further failed
add --forceadds one more.generate secret --forceandimport --forcetake the same path.Where:
addVersion):createAndSaveVersionat L225 runs beforeupdatePreviousVersionat L232, which is whereGetOrDeriveLongTermKeyfirst runs for an existing secret.Correct behaviour: a failed
addchanges nothing. Get the long-term key (and load the previous version's metadata) before writing the new version, so a wrong passphrase or mnemonic fails before anything is written; only the rename ofcurrentcan then still fail.Definition of done
add --forcefails to unlock the vault,version listshows the same versions as before and the versions directory is unchanged.AddSecretwithforceand a wrong passphrase, and requires the directory unchanged; it fails before the fix.Model: fable-5-1