fix: resolve deadlock, data races and path traversal vulnerabilities
- task.go: fix deadlock in ToStatusUpdate() — calling Percent() (which RLocks) while already holding RLock caused deadlock when a writer was waiting; compute percent inline instead - usenet.go: fix data race in Cancel() — tracker and taskDir were read without the mutex while Download() writes them under it; read all fields under the same lock - upnp.go: fix UPnP Remove() blocking shutdown — run cleanup in goroutine with 10s deadline (removeNATPMP worst case is 3s dial + 5s deadline) - daemon.go: add path traversal protection for stream requests — validate sr.FilePath is within configured directories before os.Stat; defends against compromised API server sending arbitrary paths - client.go: add wakeClient without timeout for long-poll wake endpoint where context controls cancellation - sync.go: trigger immediate sync when entering watching mode so stream requests are picked up without waiting for the next scheduled interval
This commit is contained in:
parent
78c16c295e
commit
ef4f38d324
6 changed files with 146 additions and 13 deletions
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
|
|
@ -316,7 +317,12 @@ func runDaemonStart() error {
|
|||
return
|
||||
}
|
||||
|
||||
filePath := sr.FilePath
|
||||
filePath := filepath.Clean(sr.FilePath)
|
||||
if !isAllowedStreamPath(filePath, cfg.Download.Dir, cfg.Library.ScanPath,
|
||||
cfg.Organize.MoviesDir, cfg.Organize.TVShowsDir) {
|
||||
log.Printf("[%s] stream request rejected: path outside allowed dirs: %s", agent.ShortID(sr.TaskID), filePath)
|
||||
return
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
log.Printf("[%s] stream request: file not found: %s", agent.ShortID(sr.TaskID), filePath)
|
||||
|
|
@ -443,6 +449,25 @@ func runDaemonStart() error {
|
|||
}
|
||||
}
|
||||
|
||||
// isAllowedStreamPath checks that filePath is within one of the directories
|
||||
// the daemon is configured to manage. This defends against a compromised API
|
||||
// server sending a path traversal payload (e.g. /etc/passwd) in StreamRequest.
|
||||
// isAllowedStreamPath reports whether filePath is contained within one of the
|
||||
// allowedDirs. filePath must already be cleaned (filepath.Clean) by the caller.
|
||||
// This defends against a compromised API server sending a path traversal payload.
|
||||
func isAllowedStreamPath(filePath string, allowedDirs ...string) bool {
|
||||
for _, dir := range allowedDirs {
|
||||
if dir == "" {
|
||||
continue
|
||||
}
|
||||
rel, err := filepath.Rel(filepath.Clean(dir), filePath)
|
||||
if err == nil && !strings.HasPrefix(rel, "..") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func formatSpeedLog(bps int64) string {
|
||||
switch {
|
||||
case bps >= 1024*1024*1024:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue