check / check (push) Waiting to run
init and vault create put the mnemonic into the process environment for vault.CreateVault to read back, so every program they ran, gpg included, inherited it, and SB_SECRET_MNEMONIC and SB_UNLOCK_PASSPHRASE were read at 13 places and never unset. Each command that may need them now reads both once, in its RunE, into locked buffers on the CLI Instance, and unsets them at once. The buffers are passed down: vault.CreateVault takes the mnemonic, a Vault carries Mnemonic and UnlockPassphrase, and the PGP, keychain and Secure Enclave unlocker constructors take both. Nothing below the command reads the environment. README warns against both variables. 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))
|
|
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))
|
|
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))
|
|
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")
|
|
})
|
|
}
|
|
}
|