Compare commits
1
Commits
next
...
0a35796205
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a35796205 |
@@ -0,0 +1,85 @@
|
|||||||
|
//nolint:testpackage // white-box test of unexported internals
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/awnumar/memguard"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"sneak.berlin/go/secret/internal/vault"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errRenameFailed is returned by renameFailFs in place of a successful
|
||||||
|
// rename.
|
||||||
|
var errRenameFailed = errors.New("permission denied")
|
||||||
|
|
||||||
|
// renameFailFs fails every Rename of one path, as a read-only parent
|
||||||
|
// directory does.
|
||||||
|
type renameFailFs struct {
|
||||||
|
afero.Fs
|
||||||
|
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *renameFailFs) Rename(oldname, newname string) error {
|
||||||
|
if oldname == f.path {
|
||||||
|
return errRenameFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Fs.Rename(oldname, newname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCrossVaultMoveFailsWhenSourceRemains is a regression test for
|
||||||
|
// https://git.eeqj.de/sneak/secret/issues/159, where a move between two
|
||||||
|
// vaults whose source could not be removed reported the secret moved and
|
||||||
|
// succeeded. It must fail, and the copy and the source must both be there.
|
||||||
|
// The state directory lock refuses the wrapped filesystem, so the test
|
||||||
|
// calls moveSecret, which MoveSecret runs once it holds the lock.
|
||||||
|
func TestCrossVaultMoveFailsWhenSourceRemains(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
base := afero.NewMemMapFs()
|
||||||
|
|
||||||
|
_, err := vault.CreateVault(base, testStateDir, "work",
|
||||||
|
testMnemonicBuffer(t), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
source, err := vault.CreateVault(base, testStateDir, "default",
|
||||||
|
testMnemonicBuffer(t), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = source.AddSecret("x",
|
||||||
|
memguard.NewBufferFromBytes([]byte("value")), false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
instance := NewCLIInstanceWithStateDir(&renameFailFs{
|
||||||
|
Fs: base,
|
||||||
|
path: testStateDir + "/vaults.d/default/secrets.d/x",
|
||||||
|
}, testStateDir)
|
||||||
|
instance.Mnemonic = testMnemonicBuffer(t)
|
||||||
|
|
||||||
|
err = instance.moveSecret(&cobra.Command{}, "default:x", "work:y", false)
|
||||||
|
require.ErrorIs(t, err, errRenameFailed)
|
||||||
|
require.ErrorContains(t, err, "secret 'default:x' is copied with all "+
|
||||||
|
"1 version(s) to 'work:y', but the source remains")
|
||||||
|
|
||||||
|
// requireValue requires that the vault vaultName holds secretName with
|
||||||
|
// the value stored above.
|
||||||
|
requireValue := func(vaultName, secretName string) {
|
||||||
|
vlt := vault.NewVault(base, testStateDir, vaultName)
|
||||||
|
vlt.Mnemonic = testMnemonicBuffer(t)
|
||||||
|
|
||||||
|
value, err := vlt.GetSecret(secretName)
|
||||||
|
require.NoError(t, err, vaultName+":"+secretName)
|
||||||
|
|
||||||
|
defer value.Destroy()
|
||||||
|
|
||||||
|
require.Equal(t, []byte("value"), value.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
requireValue("default", "x")
|
||||||
|
requireValue("work", "y")
|
||||||
|
}
|
||||||
@@ -1097,12 +1097,10 @@ func (cli *Instance) moveSecretCrossVault(
|
|||||||
// Delete source secret
|
// Delete source secret
|
||||||
err = secret.RemoveDirAtomic(cli.fs, srcSecretDir)
|
err = secret.RemoveDirAtomic(cli.fs, srcSecretDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Copy succeeded but delete failed - warn but don't fail
|
return fmt.Errorf("secret '%s:%s' is copied with all %d version(s) "+
|
||||||
cmd.Printf("Warning: copied secret but failed to remove source: %v\n", err)
|
"to '%s:%s', but the source remains: %w",
|
||||||
cmd.Printf("Moved secret '%s:%s' to '%s:%s' (%d version(s))\n",
|
srcVault.Name, srcSecretName, versionCount,
|
||||||
srcVault.Name, srcSecretName, destVault.Name, destSecretName, versionCount)
|
destVault.Name, destSecretName, err)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Printf("Moved secret '%s:%s' to '%s:%s' (%d version(s))\n",
|
cmd.Printf("Moved secret '%s:%s' to '%s:%s' (%d version(s))\n",
|
||||||
|
|||||||
Reference in New Issue
Block a user