diff --git a/internal/filesystem/cache.go b/internal/filesystem/cache.go index 7fb0619..b169794 100644 --- a/internal/filesystem/cache.go +++ b/internal/filesystem/cache.go @@ -83,8 +83,32 @@ func (s *CacheControlledFileServer) ServeHTTP(w http.ResponseWriter, r *http.Req } func (s *CacheControlledFileServer) setETag(w http.ResponseWriter, r *http.Request, path string) { - fullPath := filepath.Join(s.Root, filepath.Clean(path)) - info, err := os.Stat(fullPath) + cleanPath := filepath.Clean("/" + filepath.ToSlash(path)) + relPath := strings.TrimPrefix(cleanPath, "/") + if relPath == "" || relPath == "." || strings.HasPrefix(relPath, "../") || relPath == ".." { + return + } + + rootAbs, err := filepath.Abs(s.Root) + if err != nil { + return + } + + fullPath := filepath.Join(rootAbs, relPath) + fullAbs, err := filepath.Abs(fullPath) + if err != nil { + return + } + + rootWithSep := rootAbs + if !strings.HasSuffix(rootWithSep, string(os.PathSeparator)) { + rootWithSep += string(os.PathSeparator) + } + if fullAbs != rootAbs && !strings.HasPrefix(fullAbs, rootWithSep) { + return + } + + info, err := os.Stat(fullAbs) if err != nil || info.IsDir() { return } diff --git a/internal/ui/server.go b/internal/ui/server.go index 7f56557..04a8a3b 100644 --- a/internal/ui/server.go +++ b/internal/ui/server.go @@ -6,6 +6,8 @@ import ( "io/fs" "log/slog" "net/http" + "path" + "strings" "sync" "time" @@ -129,15 +131,23 @@ func (s *Server) routes() { fileServer := http.FileServer(http.FS(staticFS)) s.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // Serve static files, but for SPA routes serve index.html - path := r.URL.Path - if path == "/" || path == "" { + reqPath := r.URL.Path + cleanPath := path.Clean("/" + reqPath) + if cleanPath == "/" { + r.URL.Path = "/" + fileServer.ServeHTTP(w, r) + return + } + + relPath := strings.TrimPrefix(cleanPath, "/") + if relPath == "" || relPath == "." || relPath == ".." || strings.HasPrefix(relPath, "../") { r.URL.Path = "/" fileServer.ServeHTTP(w, r) return } // Check if file exists in static - f, err := staticFS.Open(path[1:]) // strip leading / + f, err := staticFS.Open(relPath) if err != nil { // SPA fallback: serve index.html r.URL.Path = "/" @@ -145,6 +155,7 @@ func (s *Server) routes() { return } f.Close() + r.URL.Path = cleanPath fileServer.ServeHTTP(w, r) }) }