Lock the state directory and write vault files atomically (closes #34)
check / check (push) Successful in 50s
check / check (push) Successful in 50s
Each command that changes the state directory holds one lock: flock(2) on `lock` in the state directory, dropped by the kernel if the process dies, or a process-wide mutex on the in-memory test filesystem. It covers the state directory, not each vault, because `currentvault`, `vault create` and cross-vault moves span vaults, and a lock file in a vault would be deleted by `vault remove` under a waiting command. Files go through `secret.WriteFileAtomic`; versions, new secrets and cross-vault copies are built in a temporary directory and renamed into place; removals rename out of the way first. An unlocker added under an existing unlocker's directory name is still rewritten file by file: #71. Model: opus-5-5
This commit is contained in:
@@ -25,6 +25,21 @@ Bring the repo into policy compliance in one commit:
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-03: Commands that change the state directory hold one lock
|
||||
(`flock` on `lock` in the state directory; a mutex on the in-memory
|
||||
test filesystem), so concurrent commands no longer lose versions or
|
||||
race on the current pointers. Every file is written through
|
||||
`secret.WriteFileAtomic` (temporary file, sync, rename); new
|
||||
versions, new secrets and cross-vault copies are built in a
|
||||
temporary directory and renamed into place, and removals rename out
|
||||
of the way first, so an interrupted command leaves nothing
|
||||
half-written, with one exception: an unlocker added under the
|
||||
directory name of an existing one is rewritten in place, file by
|
||||
file, and a crash part-way leaves it unable to open the vault. That
|
||||
happens to a passphrase unlocker added to a vault that has one, and
|
||||
to a PGP, keychain or Secure Enclave unlocker added on the same host
|
||||
and day as another of its type
|
||||
(https://git.eeqj.de/sneak/secret/issues/71).
|
||||
- 2026-10-02: A plain `docker build .` builds again: the size tests
|
||||
skip a case that needs more locked memory than the process can
|
||||
lock, and run every case under `script/cibuild`. The image stamps the
|
||||
@@ -94,8 +109,6 @@ Bring the repo into policy compliance in one commit:
|
||||
pgpunlocker.go:256, version.go:155); age secret key held in a
|
||||
plain string in cli/crypto.go:86,91,113; private keys exposed via
|
||||
buffer.Bytes() to GPGEncryptFunc and EncryptWithPassphrase.
|
||||
- Race conditions: no file locking in vault/secrets.go:142-176;
|
||||
non-atomic writes can leave the vault inconsistent.
|
||||
- Input validation: dots in secret names risk path traversal
|
||||
(vault/secrets.go:75-99); no maximum secret size (DoS).
|
||||
- Timing attacks: bytes.Equal passphrase compare (cli/init.go:
|
||||
|
||||
Reference in New Issue
Block a user