Skip to content

Commit 7e0df5c

Browse files
author
Datanoise
committed
security: implement safeJoin and lockdown AutoDJ filesystem access
1 parent 5b41a75 commit 7e0df5c

2 files changed

Lines changed: 57 additions & 7 deletions

File tree

server/server.go

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,28 @@ func (s *Server) buildListeners(port string) ([]net.Listener, error) {
575575
return listeners, nil
576576
}
577577

578+
func (s *Server) safeJoin(base, rel string) (string, error) {
579+
absBase, err := filepath.Abs(base)
580+
if err != nil {
581+
return "", err
582+
}
583+
584+
// Join and clean the path
585+
joined := filepath.Join(absBase, rel)
586+
587+
// Ensure the result is still within the base directory
588+
relPath, err := filepath.Rel(absBase, joined)
589+
if err != nil {
590+
return "", err
591+
}
592+
593+
if strings.HasPrefix(relPath, "..") || strings.HasPrefix(relPath, "/") {
594+
return "", fmt.Errorf("security: path traversal attempt detected")
595+
}
596+
597+
return joined, nil
598+
}
599+
578600
// multiListener fans in Accept() calls from multiple net.Listeners into one.
579601
type multiListener struct {
580602
listeners []net.Listener
@@ -2656,8 +2678,22 @@ func (s *Server) handlePlayerQueue(w http.ResponseWriter, r *http.Request) {
26562678
}
26572679

26582680
if action == "add" {
2659-
streamer.PushToQueue(path)
2660-
logrus.Debugf("AutoDJ %s: Queued song %s", mount, path)
2681+
musicDir := streamer.GetMusicDir()
2682+
rel, err := filepath.Rel(musicDir, path)
2683+
if err != nil {
2684+
http.Error(w, "Forbidden", http.StatusForbidden)
2685+
return
2686+
}
2687+
2688+
fullPath, err := s.safeJoin(musicDir, rel)
2689+
if err != nil {
2690+
logrus.WithError(err).Warnf("Security: Blocked queue addition of %s for mount %s", path, mount)
2691+
http.Error(w, "Forbidden", http.StatusForbidden)
2692+
return
2693+
}
2694+
2695+
streamer.PushToQueue(fullPath)
2696+
logrus.Debugf("AutoDJ %s: Queued song %s", mount, fullPath)
26612697
} else if action == "remove" {
26622698
var index int
26632699
fmt.Sscanf(r.FormValue("index"), "%d", &index)
@@ -2773,10 +2809,9 @@ func (s *Server) handlePlayerFiles(w http.ResponseWriter, r *http.Request) {
27732809
}
27742810

27752811
musicDir := streamer.GetMusicDir()
2776-
fullPath := filepath.Join(musicDir, subDir)
2777-
2778-
// Security: Ensure fullPath is still within musicDir
2779-
if !strings.HasPrefix(fullPath, musicDir) {
2812+
fullPath, err := s.safeJoin(musicDir, subDir)
2813+
if err != nil {
2814+
logrus.WithError(err).Warnf("Security: Blocked file browser access to %s for mount %s", subDir, mount)
27802815
http.Error(w, "Forbidden", http.StatusForbidden)
27812816
return
27822817
}
@@ -2868,7 +2903,21 @@ func (s *Server) handlePlayerPlaylistAction(w http.ResponseWriter, r *http.Reque
28682903
}
28692904

28702905
if action == "add" {
2871-
fullPath := relPath // Already absolute from frontend now
2906+
musicDir := streamer.GetMusicDir()
2907+
// Frontend sends absolute path, we convert to relative to validate via safeJoin
2908+
rel, err := filepath.Rel(musicDir, relPath)
2909+
if err != nil {
2910+
http.Error(w, "Forbidden", http.StatusForbidden)
2911+
return
2912+
}
2913+
2914+
fullPath, err := s.safeJoin(musicDir, rel)
2915+
if err != nil {
2916+
logrus.WithError(err).Warnf("Security: Blocked playlist addition of %s for mount %s", relPath, mount)
2917+
http.Error(w, "Forbidden", http.StatusForbidden)
2918+
return
2919+
}
2920+
28722921
logrus.Infof("AutoDJ %s: Attempting to add %s", mount, fullPath)
28732922

28742923
info, err := os.Stat(fullPath)

tinyice.pid

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
47474

0 commit comments

Comments
 (0)