Delete .tmp- leftovers when the next command takes the lock (closes #75)
check / check (push) Failing after 3s

A command killed part-way could leave a temporary file or directory of
secret.WriteFileAtomic or secret.TempDirFor, encrypted keys included,
for good. LockStateDir now deletes them once it holds the lock, looking
only in the directories those helpers make them in (the state
directory, each vault, each secret, each version) and only at names
that start with "." and hold ".tmp-". A vault may be named ".tmp-1", so
vaults.d and the other listed directories are not searched. Commands
that only read take no lock and delete nothing. A test shows that
`unlocker remove` removes an unlocker directory with no metadata file.

Model: opus-5-5
This commit is contained in:
2026-10-04 15:14:13 +00:00
parent 7e4e0f7806
commit ff721a87e6
5 changed files with 221 additions and 16 deletions
+25 -3
View File
@@ -4,9 +4,10 @@
// by its ID. These tests give the first unlocker, which sorts before the
// one the commands act on, metadata that is not JSON, and check that the
// commands step past it, and that it can itself be removed by its
// directory name, which `secret unlocker list` names in its warning. A
// last test checks that an unlocker whose metadata file cannot be read is
// removed by its directory name only as the last unlocker is.
// directory name, which `secret unlocker list` names in its warning, as can
// one with no metadata file. A last test checks that an unlocker whose
// metadata file cannot be read is removed by its directory name only as the
// last unlocker is.
//nolint:testpackage // white-box test of unexported internals
package cli
@@ -113,6 +114,27 @@ func TestUnlockerRemoveWithCorruptUnlocker(t *testing.T) {
}
}
// TestUnlockerRemoveWithoutMetadata asserts that a partial unlocker
// directory, one with no metadata file, is removed by its directory name
// without --force from a vault with secrets: it cannot unlock the vault, so
// removing it never removes the last unlocker.
func TestUnlockerRemoveWithoutMetadata(t *testing.T) {
t.Parallel()
fs := newListTestVault(t, 2)
vaultDir := testVaultDir(listTestVaultName)
unlockersDir := filepath.Join(vaultDir, listTestUnlockersDirName)
require.NoError(t, fs.Remove(filepath.Join(
unlockersDir, listTestUnlockerDirOne, listTestMetadataFileName)))
writeTestSecret(t, fs, vaultDir)
instance, cmd := newTestInstance(fs)
require.NoError(t, instance.UnlockersRemove(listTestUnlockerDirOne, false, cmd))
assertDirEntries(t, fs, unlockersDir, listTestUnlockerDirTwo)
}
// TestUnlockerRemoveWithUnreadableMetadata asserts that removing the only
// unlocker of a vault with secrets by its directory name, when its
// metadata file cannot be checked for or read, is refused without --force: