check / check (push) Successful in 1m26s
vault.CreateVault now checks for the vault before writing anything and fails with "vault NAME already exists" (vault.ErrVaultExists). secret init and secret vault create call it while holding the state directory lock, so two creates at once cannot both pass the check. Before, either command over an existing vault replaced its metadata, passphrase unlocker and longterm.age, so none of its secrets could be decrypted. The lock tests set up the vault "work" instead of "default", which init now refuses to create again. Model: opus-5-5
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/secret/internal/cli"
|
|
"git.eeqj.de/sneak/secret/internal/secret"
|
|
"git.eeqj.de/sneak/secret/internal/vault"
|
|
"github.com/awnumar/memguard"
|
|
"github.com/spf13/afero"
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestCreateExistingVaultChangesNothing is a regression test for
|
|
// https://git.eeqj.de/sneak/secret/issues/74, where running `secret init`
|
|
// a second time, or `secret vault create` with the name of an existing
|
|
// vault, replaced that vault's keys, so that none of its secrets could be
|
|
// decrypted any more. Each must refuse, change nothing, and leave every
|
|
// vault's secret readable through its passphrase unlocker.
|
|
func TestCreateExistingVaultChangesNothing(t *testing.T) {
|
|
t.Setenv(secret.EnvMnemonic, testMnemonic)
|
|
t.Setenv(secret.EnvUnlockPassphrase, testPassphrase)
|
|
|
|
// `secret init`, `secret vault create work`, `secret vault select
|
|
// default`, and the secret "x" in each vault. "work" is then not the
|
|
// current vault, which creating it again must not change.
|
|
fs := afero.NewMemMapFs()
|
|
c := cli.NewCLIInstanceWithStateDir(fs, testStateDir)
|
|
cmd := &cobra.Command{}
|
|
|
|
require.NoError(t, c.Init(cmd))
|
|
require.NoError(t, c.CreateVault(cmd, "work"))
|
|
require.NoError(t, c.SelectVault(cmd, "default"))
|
|
|
|
vaults, err := vault.ListVaults(fs, testStateDir)
|
|
require.NoError(t, err)
|
|
require.Len(t, vaults, 2)
|
|
|
|
for _, name := range vaults {
|
|
value := memguard.NewBufferFromBytes([]byte("value"))
|
|
err := vault.NewVault(fs, testStateDir, name).AddSecret("x", value, false)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
before := snapshotStateDir(t, fs)
|
|
|
|
tests := []struct {
|
|
command string
|
|
want string
|
|
run func(c *cli.Instance) error
|
|
}{
|
|
{
|
|
"init",
|
|
"failed to create default vault: vault default already exists",
|
|
func(c *cli.Instance) error { return c.Init(cmd) },
|
|
},
|
|
{
|
|
"vault create default",
|
|
"vault default already exists",
|
|
func(c *cli.Instance) error { return c.CreateVault(cmd, "default") },
|
|
},
|
|
{
|
|
"vault create work",
|
|
"vault work already exists",
|
|
func(c *cli.Instance) error { return c.CreateVault(cmd, "work") },
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.command, func(t *testing.T) {
|
|
fs := newFsFromSnapshot(t, before)
|
|
|
|
err := tt.run(cli.NewCLIInstanceWithStateDir(fs, testStateDir))
|
|
|
|
require.EqualError(t, err, tt.want)
|
|
require.Equal(t, before, snapshotStateDir(t, fs))
|
|
|
|
// Without the mnemonic, reading a secret goes through the
|
|
// vault's passphrase unlocker.
|
|
t.Setenv(secret.EnvMnemonic, "")
|
|
|
|
for _, name := range vaults {
|
|
value, err := vault.NewVault(fs, testStateDir, name).GetSecret("x")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "value", string(value))
|
|
}
|
|
})
|
|
}
|
|
}
|