check / check (push) Failing after 2s
vault.CreateVault takes the unlocker passphrase and writes the vault directory, its metadata, long-term public key and passphrase unlocker into a temporary directory, renames that into vaults.d once complete, and only then makes the vault current. secret init and secret vault create call it once instead of adding the unlocker afterwards, so a kill part-way leaves either no vault, whose temporary directory the next command that takes the lock deletes, or a complete one. A test records the state directory before every change the call makes and checks each state, and the command run again from it. Model: opus-5-5
95 lines
2.5 KiB
Go
95 lines
2.5 KiB
Go
package vault_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.eeqj.de/sneak/secret/internal/vault"
|
|
"github.com/awnumar/memguard"
|
|
"github.com/spf13/afero"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestGetSecretVersionRejectsPathTraversal verifies that GetSecretVersion
|
|
// validates the secret name and rejects path traversal attempts.
|
|
// This is a regression test for https://git.eeqj.de/sneak/secret/issues/13
|
|
func TestGetSecretVersionRejectsPathTraversal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := afero.NewMemMapFs()
|
|
|
|
vlt, err := vault.CreateVault(fs, testStateDir, testVaultName,
|
|
testMnemonicBuffer(t), nil)
|
|
require.NoError(t, err)
|
|
|
|
// Add a legitimate secret so the vault is set up
|
|
value := memguard.NewBufferFromBytes([]byte("legitimate-secret"))
|
|
err = vlt.AddSecret("legit", value, false)
|
|
require.NoError(t, err)
|
|
|
|
// These names contain path traversal and should be rejected
|
|
maliciousNames := []string{
|
|
"../../../etc/passwd",
|
|
"..%2f..%2fetc/passwd",
|
|
".secret",
|
|
"../sibling-vault/secrets.d/target",
|
|
"foo/../bar",
|
|
"a/../../etc/passwd",
|
|
}
|
|
|
|
for _, name := range maliciousNames {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := vlt.GetSecretVersion(name, "")
|
|
require.Error(t, err,
|
|
"GetSecretVersion should reject malicious name: %s", name)
|
|
require.Contains(t, err.Error(), "invalid secret name",
|
|
"error should indicate invalid name for: %s", name)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetSecretRejectsPathTraversal verifies GetSecret (which calls
|
|
// GetSecretVersion) also rejects path traversal names.
|
|
func TestGetSecretRejectsPathTraversal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := afero.NewMemMapFs()
|
|
|
|
vlt, err := vault.CreateVault(fs, testStateDir, testVaultName,
|
|
testMnemonicBuffer(t), nil)
|
|
require.NoError(t, err)
|
|
|
|
_, err = vlt.GetSecret("../../../etc/passwd")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid secret name")
|
|
}
|
|
|
|
// TestGetSecretObjectRejectsPathTraversal verifies GetSecretObject
|
|
// also validates names and rejects path traversal attempts.
|
|
func TestGetSecretObjectRejectsPathTraversal(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fs := afero.NewMemMapFs()
|
|
|
|
vlt, err := vault.CreateVault(fs, testStateDir, testVaultName,
|
|
testMnemonicBuffer(t), nil)
|
|
require.NoError(t, err)
|
|
|
|
maliciousNames := []string{
|
|
"../../../etc/passwd",
|
|
"foo/../bar",
|
|
"a/../../etc/passwd",
|
|
}
|
|
|
|
for _, name := range maliciousNames {
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := vlt.GetSecretObject(name)
|
|
require.Error(t, err, "GetSecretObject should reject: %s", name)
|
|
require.Contains(t, err.Error(), "invalid secret name")
|
|
})
|
|
}
|
|
}
|