check / check (push) Failing after 1s
A passphrase unlocker added to a vault that had one, and a PGP, keychain or Secure Enclave unlocker added on the same day as another of its type, were written into the existing unlocker's directory file by file, so a crash part-way left a current unlocker whose files did not belong together. Unlocker directories, keychain items and Secure Enclave keys are now named with the time to the nanosecond, and secret.WriteDir refuses a directory that exists. Adding a passphrase unlocker writes the new one, points current-unlocker at it, and only then removes the vault's other passphrase unlockers. Model: opus-5-5
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
package secret_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/secret/internal/secret"
|
|
"git.eeqj.de/sneak/secret/internal/vault"
|
|
"github.com/awnumar/memguard"
|
|
"github.com/spf13/afero"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// The GPG key ID and fingerprint passed to CreatePGPUnlocker.
|
|
const (
|
|
testGPGKeyID = "0123456789ABCDEF"
|
|
testGPGFingerprint = "0123456789ABCDEF0123456789ABCDEF01234567"
|
|
)
|
|
|
|
// fakeGPGScript is a gpg for which `gpg --version` succeeds and anything
|
|
// else fails.
|
|
const fakeGPGScript = `#!/bin/sh
|
|
[ "$*" = --version ]
|
|
`
|
|
|
|
// installFakeGPG makes fakeGPGScript the only gpg on PATH for the test.
|
|
func installFakeGPG(t *testing.T) {
|
|
t.Helper()
|
|
|
|
dir := t.TempDir()
|
|
|
|
//nolint:gosec // G306: the script must be executable
|
|
err := os.WriteFile(filepath.Join(dir, "gpg"), []byte(fakeGPGScript), 0o700)
|
|
require.NoError(t, err)
|
|
|
|
t.Setenv("PATH", dir)
|
|
}
|
|
|
|
// TestCreatePGPUnlockerFailureWritesNothing makes CreatePGPUnlocker fail at
|
|
// getting the vault's long-term key, which used to come after part of the
|
|
// unlocker was written, and asserts that nothing is written. Getting the key
|
|
// fails because there is no mnemonic and no current unlocker.
|
|
//
|
|
//nolint:paralleltest // installFakeGPG uses t.Setenv
|
|
func TestCreatePGPUnlockerFailureWritesNothing(t *testing.T) {
|
|
installFakeGPG(t)
|
|
|
|
base := afero.NewMemMapFs()
|
|
vlt, err := vault.CreateVault(base, testVaultStateDir, testVaultName, nil)
|
|
require.NoError(t, err)
|
|
|
|
fs := hookFs{Fs: base, before: func(_, path string) error {
|
|
t.Errorf("changed %s", path)
|
|
|
|
return nil
|
|
}}
|
|
|
|
_, err = secret.CreatePGPUnlocker(
|
|
fs, testVaultStateDir, testGPGKeyID, testGPGFingerprint, nil, nil)
|
|
require.Error(t, err)
|
|
|
|
vaultDir, err := vlt.GetDirectory()
|
|
require.NoError(t, err)
|
|
assert.Empty(t, dirNames(t, base, filepath.Join(vaultDir, "unlockers.d")))
|
|
}
|
|
|
|
// TestPGPUnlockerAddedTwiceKeepsFirst adds two PGP unlockers one right after
|
|
// the other, so on the same host and day, and checks that the second gets a
|
|
// directory of its own and leaves the first one's files as they were.
|
|
// CreatePGPUnlocker does not check whether the GPG key already has an
|
|
// unlocker, so the test key serves for both.
|
|
//
|
|
//nolint:paralleltest // installFakeGPG uses t.Setenv
|
|
func TestPGPUnlockerAddedTwiceKeepsFirst(t *testing.T) {
|
|
installFakeGPG(t)
|
|
|
|
original := secret.GPGEncryptFunc
|
|
|
|
t.Cleanup(func() { secret.GPGEncryptFunc = original })
|
|
|
|
// Stands in for gpg, which the test does not have: "encrypts" by copying
|
|
secret.GPGEncryptFunc = func(data *memguard.LockedBuffer, _ string) ([]byte, error) {
|
|
return []byte(data.String()), nil
|
|
}
|
|
|
|
fs := afero.NewMemMapFs()
|
|
mnemonic := testMnemonicBuffer(t)
|
|
_, err := vault.CreateVault(fs, testVaultStateDir, testVaultName, mnemonic)
|
|
require.NoError(t, err)
|
|
|
|
first, err := secret.CreatePGPUnlocker(
|
|
fs, testVaultStateDir, testGPGKeyID, testGPGFingerprint, mnemonic, nil)
|
|
require.NoError(t, err)
|
|
|
|
firstFiles := dirFiles(t, fs, first.GetDirectory())
|
|
|
|
second, err := secret.CreatePGPUnlocker(
|
|
fs, testVaultStateDir, testGPGKeyID, testGPGFingerprint, mnemonic, nil)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, first.GetDirectory(), second.GetDirectory())
|
|
assert.Equal(t, firstFiles, dirFiles(t, fs, first.GetDirectory()))
|
|
}
|