fix(agentci): split path sanitising and validation
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 07:09:27 +00:00
parent ec8149efbe
commit f2c9cb39d0
2 changed files with 20 additions and 9 deletions

View file

@ -16,28 +16,36 @@ import (
var safeNameRegex = regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]+$`)
// SanitizePath ensures a filename or directory name is safe and prevents path traversal.
// Returns the validated input unchanged.
// Returns the validated basename.
// Usage: SanitizePath(...)
func SanitizePath(input string) (string, error) {
if input == "" {
return "", coreerr.E("agentci.SanitizePath", "path element is required", nil)
}
if strings.ContainsAny(input, `/\`) {
return "", coreerr.E("agentci.SanitizePath", "path separators are not allowed: "+input, nil)
}
if input == "." || input == ".." {
safeName := filepath.Base(input)
if safeName == "." || safeName == ".." {
return "", coreerr.E("agentci.SanitizePath", "invalid path element: "+input, nil)
}
if !safeNameRegex.MatchString(input) {
if strings.ContainsAny(safeName, `/\`) {
return "", coreerr.E("agentci.SanitizePath", "path separators are not allowed: "+input, nil)
}
if !safeNameRegex.MatchString(safeName) {
return "", coreerr.E("agentci.SanitizePath", "invalid characters in path element: "+input, nil)
}
return input, nil
return safeName, nil
}
// ValidatePathElement validates a single local path element and returns its safe form.
// Usage: ValidatePathElement(...)
func ValidatePathElement(input string) (string, error) {
return SanitizePath(input)
safeName, err := SanitizePath(input)
if err != nil {
return "", err
}
if safeName != input {
return "", coreerr.E("agentci.ValidatePathElement", "path separators are not allowed: "+input, nil)
}
return safeName, nil
}
// ResolvePathWithinRoot resolves a validated path element beneath a root directory.

View file

@ -21,6 +21,9 @@ func TestSanitizePath_Good(t *testing.T) {
{"with.dot", "with.dot"},
{"CamelCase", "CamelCase"},
{"123", "123"},
{"../secret", "secret"},
{"/var/tmp/report.txt", "report.txt"},
{"nested/path/file", "file"},
}
for _, tt := range tests {
@ -44,8 +47,8 @@ func TestSanitizePath_Bad(t *testing.T) {
{"pipe", "file|name"},
{"ampersand", "file&name"},
{"dollar", "file$name"},
{"slash", "path/to/file.txt"},
{"backslash", `path\to\file.txt`},
{"current dir", "."},
{"parent traversal base", ".."},
{"root", "/"},
{"empty", ""},