security: harden recursive file operations
This commit is contained in:
@@ -326,6 +326,7 @@ func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
||||
if len(items) == 0 {
|
||||
items = r.Form["path"]
|
||||
}
|
||||
paths := make([]string, 0, len(items))
|
||||
for _, raw := range items {
|
||||
if unsafePath(raw) {
|
||||
writeJSONError(w, http.StatusBadRequest, "invalid parameter")
|
||||
@@ -340,6 +341,11 @@ func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSONError(w, http.StatusForbidden, "has no privilege")
|
||||
return
|
||||
}
|
||||
paths = append(paths, p)
|
||||
}
|
||||
// Preflight every target before mutating anything, so a multi-file delete
|
||||
// cannot partially succeed when a later item is forbidden.
|
||||
for _, p := range paths {
|
||||
if e := s.removePath(p); e != nil {
|
||||
http.Error(w, e.Error(), 500)
|
||||
return
|
||||
@@ -881,6 +887,12 @@ func davSafePath(name string) (string, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func sameOrDescendantPath(src, dst string) bool {
|
||||
src = strings.Trim(path.Clean("/"+src), "/")
|
||||
dst = strings.Trim(path.Clean("/"+dst), "/")
|
||||
return src == dst || (src != "" && strings.HasPrefix(dst, src+"/"))
|
||||
}
|
||||
|
||||
func (f *webdavStorageFS) info(name string) (*webdav.FileInfo, error) {
|
||||
p, err := davSafePath(name)
|
||||
if err != nil {
|
||||
@@ -1023,6 +1035,9 @@ func (f *webdavStorageFS) Copy(ctx context.Context, name, dest string, opts *web
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if sameOrDescendantPath(src, dst) {
|
||||
return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source"))
|
||||
}
|
||||
if opts != nil && opts.NoOverwrite {
|
||||
if _, err := f.store.Stat(dst); err == nil {
|
||||
return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists"))
|
||||
@@ -1088,6 +1103,9 @@ func (f *webdavStorageFS) Move(ctx context.Context, name, dest string, opts *web
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if sameOrDescendantPath(src, dst) {
|
||||
return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source"))
|
||||
}
|
||||
if opts == nil || opts.NoOverwrite {
|
||||
if _, err := f.store.Stat(dst); err == nil {
|
||||
return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists"))
|
||||
|
||||
Reference in New Issue
Block a user