security: reject WebDAV virtual-root mutation

This commit is contained in:
OpenClaw Agent
2026-09-21 22:38:36 +08:00
parent 18d5d29a09
commit cfe7295a96
+26 -4
View File
@@ -981,6 +981,9 @@ func (f *webdavStorageFS) Create(ctx context.Context, name string, body io.ReadC
if err != nil {
return nil, false, err
}
if isVirtualStorageRoot(f.store, p) {
return nil, false, virtualRootMutationError()
}
_, statErr := f.store.Stat(p)
if statErr != nil && !os.IsNotExist(statErr) {
return nil, false, statErr
@@ -1002,13 +1005,26 @@ func (f *webdavStorageFS) Create(ctx context.Context, name string, body io.ReadC
return fi, true, err
}
func isVirtualStorageRoot(store storage.Storage, p string) bool {
m, ok := store.(storage.Multi)
if !ok {
return false
}
_, rel, found := m.FindRoot(p)
return found && rel == ""
}
func virtualRootMutationError() error {
return webdav.NewHTTPError(http.StatusForbidden, fmt.Errorf("cannot modify virtual root"))
}
func (f *webdavStorageFS) RemoveAll(ctx context.Context, name string, opts *webdav.RemoveAllOptions) error {
p, err := davSafePath(name)
if err != nil {
return err
}
if p == "" {
return fmt.Errorf("cannot remove storage root")
if p == "" || isVirtualStorageRoot(f.store, p) {
return virtualRootMutationError()
}
if _, err := f.store.Stat(p); err != nil {
if os.IsNotExist(err) {
@@ -1038,8 +1054,8 @@ func (f *webdavStorageFS) Mkdir(ctx context.Context, name string) error {
if err != nil {
return err
}
if p == "" {
return fmt.Errorf("cannot create storage root")
if p == "" || isVirtualStorageRoot(f.store, p) {
return virtualRootMutationError()
}
if _, err := f.store.Stat(p); err == nil {
return webdav.NewHTTPError(http.StatusMethodNotAllowed, fmt.Errorf("resource already exists"))
@@ -1061,6 +1077,9 @@ func (f *webdavStorageFS) Copy(ctx context.Context, name, dest string, opts *web
if sameOrDescendantPath(src, dst) {
return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source"))
}
if isVirtualStorageRoot(f.store, src) || isVirtualStorageRoot(f.store, dst) {
return false, virtualRootMutationError()
}
_, dstErr := f.store.Stat(dst)
dstExists := dstErr == nil
if dstErr != nil && !os.IsNotExist(dstErr) {
@@ -1130,6 +1149,9 @@ func (f *webdavStorageFS) Move(ctx context.Context, name, dest string, opts *web
if sameOrDescendantPath(src, dst) {
return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source"))
}
if isVirtualStorageRoot(f.store, src) || isVirtualStorageRoot(f.store, dst) {
return false, virtualRootMutationError()
}
if _, err := f.store.Stat(dst); err == nil {
// CHFS v4 MOVE does not overwrite an existing destination, even when
// Overwrite is omitted/T and the caller has delete permission.