From f99e735966aa77626d28f2951caedde417e151a5 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Mon, 21 Sep 2026 20:50:51 +0800 Subject: [PATCH] security: reject traversal segments before permission checks --- chfs-ng/internal/api/server.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index d842e37..639e630 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -123,6 +123,16 @@ func writeJSONError(w http.ResponseWriter, status int, message string) { w.WriteHeader(status) _ = json.NewEncoder(w).Encode(map[string]string{"error": message}) } +func unsafePath(p string) bool { + p = strings.ReplaceAll(p, "\\", "/") + for _, part := range strings.Split(p, "/") { + if part == ".." { + return true + } + } + return false +} + func qpath(r *http.Request) string { return strings.Trim(strings.ReplaceAll(r.URL.Query().Get("filepath"), "\\", "/"), "/") } @@ -167,6 +177,10 @@ func writeCreated(w http.ResponseWriter) { w.WriteHeader(http.StatusCreated) } func (s *Server) files(w http.ResponseWriter, r *http.Request) { a := account(r, s) + if unsafePath(r.URL.Query().Get("filepath")) { + writeJSONError(w, http.StatusBadRequest, "invalid parameter") + return + } p := qpath(r) m := a.MaskFor(p) if !m.HasRead() { @@ -213,6 +227,10 @@ func (s *Server) newdir(w http.ResponseWriter, r *http.Request) { _ = r.ParseMultipartForm(16 << 20) _ = r.ParseForm() p := qpath(r) + if unsafePath(r.URL.Query().Get("filepath")) || unsafePath(r.FormValue("filepath")) { + writeJSONError(w, http.StatusBadRequest, "invalid parameter") + return + } if p == "" { p = strings.Trim(strings.ReplaceAll(r.FormValue("filepath"), "\\", "/"), "/") } @@ -536,6 +554,10 @@ func (s *Server) shared(w http.ResponseWriter, r *http.Request) { return } p := strings.Trim(strings.TrimPrefix(r.URL.Path, "/chfs/shared/"), "/") + if unsafePath(p) { + http.NotFound(w, r) + return + } fi, err := s.Store.Stat(p) if err != nil { http.NotFound(w, r) @@ -716,6 +738,10 @@ func (s *Server) webdav(w http.ResponseWriter, r *http.Request) { return } vp := strings.Trim(strings.TrimPrefix(r.URL.Path, "/webdav"), "/") + if unsafePath(vp) { + writeJSONError(w, http.StatusBadRequest, "invalid parameter") + return + } old := r.URL.Path r.URL.Path = "/" + vp defer func() { r.URL.Path = old }()