This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// Package pathutil contains lexical path-policy helpers. It does not replace
|
||||
// descriptor-anchored filesystem access for security-sensitive operations.
|
||||
package pathutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Within reports whether path is parent itself or a lexical descendant.
|
||||
func Within(path, parent string) bool {
|
||||
relative, err := filepath.Rel(parent, path)
|
||||
return err == nil && relative != ".." && !strings.HasPrefix(relative, ".."+string(filepath.Separator))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package pathutil_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"proxyctl/internal/pathutil"
|
||||
)
|
||||
|
||||
func TestWithinUsesComponentBoundaries(t *testing.T) {
|
||||
parent := filepath.Join(string(filepath.Separator), "home", "alice")
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
path string
|
||||
want bool
|
||||
}{
|
||||
{name: "same", path: parent, want: true},
|
||||
{name: "child", path: filepath.Join(parent, ".config", "proxyctl"), want: true},
|
||||
{name: "cleaned child", path: filepath.Join(parent, "tmp", "..", ".config"), want: true},
|
||||
{name: "parent", path: filepath.Dir(parent), want: false},
|
||||
{name: "sibling prefix", path: parent + "-backup", want: false},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := pathutil.Within(test.path, parent); got != test.want {
|
||||
t.Fatalf("Within(%q, %q) = %v, want %v", test.path, parent, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user