Files
secret/internal/vault/path_traversal_test.go
T
clawbot 176095e3d1
check / check (push) Failing after 1s
Check errors by identity, not by message text, in tests (closes #49)
Tests that asserted a failure by a fragment of its message now use
errors.Is: a refactor returning the wrong error, or wrapping with %v
instead of %w, now fails them. New tests return each exported error of
internal/vault and pkg/bip85 that no test returned, and check wrapped
causes (os.ErrNotExist, ErrMnemonicMismatch through GetSecret,
ErrInvalidPathComponent through DeriveBIP85Entropy). The 999-versions
test moves into package secret to name its unexported error. Checks of
errors no test can name keep their text; they are listed on the issue.

Model: opus-5-5
2026-10-04 23:25:06 +02:00

92 lines
2.4 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.ErrorIs(t, err, vault.ErrInvalidSecretName,
"GetSecretVersion should reject malicious name: %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.ErrorIs(t, err, vault.ErrInvalidSecretName)
}
// 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.ErrorIs(t, err, vault.ErrInvalidSecretName,
"GetSecretObject should reject: %s", name)
})
}
}