fix(volume-browser): address security review findings
Critical fixes: - IDOR: verify volume belongs to project before resolving path - Upload: override global 1MB body limit for upload endpoint (100MB) High-priority fixes: - Symlink escape: use filepath.EvalSymlinks in safePath validation - Remove host filesystem path from browse API response - Sanitize Content-Disposition filenames, force application/octet-stream - Strip directory components from upload filenames
This commit is contained in:
@@ -168,6 +168,7 @@ func SaveFile(rootPath, relativePath string, r io.Reader) error {
|
||||
}
|
||||
|
||||
// safePath resolves a relative path within rootPath and validates it doesn't escape.
|
||||
// Resolves symlinks to prevent symlink-based traversal attacks.
|
||||
func safePath(rootPath, relativePath string) (string, error) {
|
||||
if relativePath == "" {
|
||||
return rootPath, nil
|
||||
@@ -181,15 +182,24 @@ func safePath(rootPath, relativePath string) (string, error) {
|
||||
|
||||
absPath := filepath.Join(rootPath, cleaned)
|
||||
|
||||
// Double-check the resolved path is within the root.
|
||||
// Resolve the root path (follow symlinks in the root itself).
|
||||
absRoot, err := filepath.Abs(rootPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve root: %w", err)
|
||||
}
|
||||
if realRoot, err := filepath.EvalSymlinks(absRoot); err == nil {
|
||||
absRoot = realRoot
|
||||
}
|
||||
|
||||
// Resolve the target path including symlinks.
|
||||
absResolved, err := filepath.Abs(absPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve path: %w", err)
|
||||
}
|
||||
if realResolved, err := filepath.EvalSymlinks(absResolved); err == nil {
|
||||
absResolved = realResolved
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(absResolved, absRoot) {
|
||||
return "", fmt.Errorf("path traversal not allowed")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user