From 3d865259fc4ad1d76aec8cac4feb6e4b3d31b299 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Mon, 21 Sep 2026 21:19:55 +0800 Subject: [PATCH] security: require delete permission for HTTP rename overwrite --- chfs-ng/internal/api/server.go | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index ad52585..768a81f 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -263,6 +263,30 @@ func (s *Server) newdir(w http.ResponseWriter, r *http.Request) { s.logOp(r, fmt.Sprintf("created directory '%s'", p)) writeCreated(w) } +func checkDeleteTree(store storage.Storage, a *auth.Account, root string) error { + info, err := store.Stat(root) + if err != nil { + return err + } + if !a.MaskFor(root).HasDelete() { + return fmt.Errorf("delete permission denied") + } + if !info.IsDir() { + return nil + } + items, err := store.List(root) + if err != nil { + return err + } + for _, item := range items { + p := path.Join(root, item.Name()) + if err := checkDeleteTree(store, a, p); err != nil { + return err + } + } + return nil +} + func (s *Server) rename(w http.ResponseWriter, r *http.Request) { a := account(r, s) rawOld, rawNew := r.FormValue("old"), r.FormValue("new") @@ -276,6 +300,17 @@ func (s *Server) rename(w http.ResponseWriter, r *http.Request) { writeJSONError(w, http.StatusForbidden, "has no privilege") return } + if old != neu { + if _, e := s.Store.Stat(neu); e == nil { + if e := checkDeleteTree(s.Store, a, neu); e != nil { + writeJSONError(w, http.StatusForbidden, "has no privilege") + return + } + } else if !os.IsNotExist(e) { + http.Error(w, e.Error(), 500) + return + } + } if e := s.Store.Rename(old, neu); e != nil { http.Error(w, e.Error(), 500) return