package vault import ( "testing" "git.eeqj.de/sneak/secret/internal/secret" "github.com/awnumar/memguard" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "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) { testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" t.Setenv(secret.EnvMnemonic, testMnemonic) t.Setenv(secret.EnvUnlockPassphrase, "test-passphrase") fs := afero.NewMemMapFs() stateDir := "/test/state" vlt, err := CreateVault(fs, stateDir, "test-vault") 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) { _, err := vlt.GetSecretVersion(name, "") assert.Error(t, err, "GetSecretVersion should reject malicious name: %s", name) assert.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) { testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" t.Setenv(secret.EnvMnemonic, testMnemonic) t.Setenv(secret.EnvUnlockPassphrase, "test-passphrase") fs := afero.NewMemMapFs() stateDir := "/test/state" vlt, err := CreateVault(fs, stateDir, "test-vault") require.NoError(t, err) _, err = vlt.GetSecret("../../../etc/passwd") assert.Error(t, err) assert.Contains(t, err.Error(), "invalid secret name") } // TestGetSecretObjectRejectsPathTraversal verifies GetSecretObject // also validates names and rejects path traversal attempts. func TestGetSecretObjectRejectsPathTraversal(t *testing.T) { testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" t.Setenv(secret.EnvMnemonic, testMnemonic) t.Setenv(secret.EnvUnlockPassphrase, "test-passphrase") fs := afero.NewMemMapFs() stateDir := "/test/state" vlt, err := CreateVault(fs, stateDir, "test-vault") require.NoError(t, err) maliciousNames := []string{ "../../../etc/passwd", "foo/../bar", "a/../../etc/passwd", } for _, name := range maliciousNames { t.Run(name, func(t *testing.T) { _, err := vlt.GetSecretObject(name) assert.Error(t, err, "GetSecretObject should reject: %s", name) assert.Contains(t, err.Error(), "invalid secret name") }) } }