Search, inspect, stream, and download torrents from the terminal. Replaces the entire *arr stack with a single binary.
37 lines
1.2 KiB
Go
37 lines
1.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// isWithinDir checks that resolved is a child of baseDir (prevents path traversal).
|
|
// Both paths must be absolute and clean.
|
|
func isWithinDir(baseDir, resolved string) bool {
|
|
base := filepath.Clean(baseDir)
|
|
target := filepath.Clean(resolved)
|
|
return target == base || strings.HasPrefix(target, base+string(filepath.Separator))
|
|
}
|
|
|
|
// safePath constructs a path under baseDir and validates it doesn't escape.
|
|
// Returns an error if the resulting path is outside baseDir.
|
|
// If the resulting path exists and is a symlink that resolves outside baseDir,
|
|
// it is also rejected.
|
|
func safePath(baseDir, untrusted string) (string, error) {
|
|
resolved := filepath.Join(baseDir, untrusted) // Join already cleans
|
|
|
|
if !isWithinDir(baseDir, resolved) {
|
|
return "", fmt.Errorf("path traversal blocked: %q escapes %q", untrusted, baseDir)
|
|
}
|
|
|
|
// Resolve symlinks if the path already exists on disk
|
|
if real, err := filepath.EvalSymlinks(resolved); err == nil {
|
|
if !isWithinDir(baseDir, real) {
|
|
return "", fmt.Errorf("path traversal blocked: %q resolves outside %q via symlink", untrusted, baseDir)
|
|
}
|
|
return real, nil
|
|
}
|
|
|
|
return resolved, nil
|
|
}
|