@@ -7,6 +7,31 @@ import (
77 "github.com/indaco/sley/internal/apperrors"
88)
99
10+ // resolveSymlinks attempts to resolve symlinks for both paths.
11+ // On case-insensitive filesystems (e.g., macOS), /var may be a symlink to /private/var.
12+ // To avoid false mismatches, both paths are resolved together:
13+ // if either resolution fails, the originals are returned unchanged.
14+ func resolveSymlinks (absBase , absPath string ) (string , string ) {
15+ resolvedBase , baseErr := filepath .EvalSymlinks (absBase )
16+ if baseErr != nil {
17+ return absBase , absPath
18+ }
19+
20+ // Try resolving the full path first
21+ if resolvedPath , err := filepath .EvalSymlinks (absPath ); err == nil {
22+ return resolvedBase , resolvedPath
23+ }
24+
25+ // Path doesn't exist yet; try resolving parent directory and reattach the filename
26+ parent := filepath .Dir (absPath )
27+ if resolvedParent , err := filepath .EvalSymlinks (parent ); err == nil {
28+ return resolvedBase , filepath .Join (resolvedParent , filepath .Base (absPath ))
29+ }
30+
31+ // Cannot resolve path side — fall back to both unresolved to avoid mismatch
32+ return absBase , absPath
33+ }
34+
1035// ValidatePath ensures a path is safe and within expected boundaries.
1136// It rejects paths with directory traversal attempts and cleans the path.
1237func ValidatePath (path string , baseDir string ) (string , error ) {
@@ -29,6 +54,9 @@ func ValidatePath(path string, baseDir string) (string, error) {
2954 return "" , & apperrors.PathValidationError {Path : path , Reason : "invalid path" }
3055 }
3156
57+ // Resolve symlinks to normalize paths on case-insensitive filesystems
58+ absBase , absPath = resolveSymlinks (absBase , absPath )
59+
3260 // Check for directory traversal
3361 if ! strings .HasPrefix (absPath , absBase + string (filepath .Separator )) && absPath != absBase {
3462 return "" , & apperrors.PathValidationError {Path : path , Reason : "path traversal detected" }
@@ -39,6 +67,7 @@ func ValidatePath(path string, baseDir string) (string, error) {
3967}
4068
4169// IsWithinDir checks if a path is within a given directory.
70+ // Resolves symlinks to handle case-insensitive filesystems correctly.
4271func IsWithinDir (path string , dir string ) bool {
4372 absPath , err := filepath .Abs (path )
4473 if err != nil {
@@ -50,5 +79,8 @@ func IsWithinDir(path string, dir string) bool {
5079 return false
5180 }
5281
82+ // Resolve symlinks to normalize paths on case-insensitive filesystems
83+ absDir , absPath = resolveSymlinks (absDir , absPath )
84+
5385 return strings .HasPrefix (absPath , absDir + string (filepath .Separator )) || absPath == absDir
5486}
0 commit comments