fix: block .. path components in secret names and validate in GetSecretObject
- isValidSecretName() now rejects names with '..' path components (e.g. foo/../bar) - GetSecretObject() now calls isValidSecretName() before building paths - Added test cases for mid-path traversal patterns
This commit is contained in:
parent
3fd30bb9e6
commit
8eb25b98fd
@ -35,6 +35,8 @@ func TestGetSecretVersionRejectsPathTraversal(t *testing.T) {
|
|||||||
"..%2f..%2fetc/passwd",
|
"..%2f..%2fetc/passwd",
|
||||||
".secret",
|
".secret",
|
||||||
"../sibling-vault/secrets.d/target",
|
"../sibling-vault/secrets.d/target",
|
||||||
|
"foo/../bar",
|
||||||
|
"a/../../etc/passwd",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range maliciousNames {
|
for _, name := range maliciousNames {
|
||||||
@ -64,3 +66,31 @@ func TestGetSecretRejectsPathTraversal(t *testing.T) {
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "invalid secret name")
|
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")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -92,6 +92,13 @@ func isValidSecretName(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for path traversal via ".." components
|
||||||
|
for _, part := range strings.Split(name, "/") {
|
||||||
|
if part == ".." {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check the basic pattern
|
// Check the basic pattern
|
||||||
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_\/]+$`, name)
|
matched, _ := regexp.MatchString(`^[a-z0-9\.\-\_\/]+$`, name)
|
||||||
|
|
||||||
@ -460,6 +467,10 @@ func (v *Vault) UnlockVault() (*age.X25519Identity, error) {
|
|||||||
|
|
||||||
// GetSecretObject retrieves a Secret object with metadata loaded from this vault
|
// GetSecretObject retrieves a Secret object with metadata loaded from this vault
|
||||||
func (v *Vault) GetSecretObject(name string) (*secret.Secret, error) {
|
func (v *Vault) GetSecretObject(name string) (*secret.Secret, error) {
|
||||||
|
if !isValidSecretName(name) {
|
||||||
|
return nil, fmt.Errorf("invalid secret name: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
// First check if the secret exists by checking for the metadata file
|
// First check if the secret exists by checking for the metadata file
|
||||||
vaultDir, err := v.GetDirectory()
|
vaultDir, err := v.GetDirectory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user