Keep the keychain unlocker passphrase in locked memory (closes #36)
check / check (push) Successful in 39s
check / check (push) Successful in 39s
The passphrase protecting the keychain unlocker's age key was a plain string passed through encoding/json, leaving copies in ordinary memory when an unlocker was created and each time one was used. It is now generated into a locked buffer, and KeychainData, moved to keychaindata.go, which is not darwin-only so its tests run on Linux, writes and reads the keychain JSON itself: encode copies the parts straight into a locked buffer, and decodeKeychainData takes the passphrase from a json.RawMessage that it wipes. The JSON field names are unchanged. keychainunlocker.go only calls this code and stores the item from the locked buffer without a string copy. Model: opus-5-5
This commit was merged in pull request #63.
This commit is contained in:
@@ -25,6 +25,11 @@ Bring the repo into policy compliance in one commit:
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-03: The keychain unlocker's age key passphrase stays in
|
||||
locked memory: it is generated into a locked buffer, and the
|
||||
keychain JSON is written and read by `KeychainData` code in
|
||||
`internal/secret/keychaindata.go` (tested on Linux) without
|
||||
`encoding/json` holding it; the JSON field names are unchanged.
|
||||
- 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
|
||||
@@ -88,12 +93,11 @@ Bring the repo into policy compliance in one commit:
|
||||
- Command injection: GPG key IDs passed unescaped to exec.Command
|
||||
(pgpunlocker.go:323-327); data.String() passed unescaped to the
|
||||
security command (keychainunlocker.go:472-476).
|
||||
- Memory security: KeychainData stores AgePrivKeyPassphrase as a
|
||||
plain string (keychainunlocker.go:342,393-396); age identity
|
||||
.String() creates unprotected copies (keychainunlocker.go:356,
|
||||
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.
|
||||
- Memory security: age identity .String() creates unprotected
|
||||
copies (keychainunlocker.go:356, 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
|
||||
|
||||
Reference in New Issue
Block a user