filebrowser/storage/bolt/share.go
Henrique Dias f30fca636c
fix(share): delete exact directory share on trailing-slash delete (GHSA-pp88-jhwj-5qh5)
DeleteWithPathPrefix queried the share index with the raw path, so deleting a
directory through a trailing-slash path (e.g. DELETE /api/resources/a/) only
matched descendants like /a/child and missed the exact /a share, leaving it in
storage. If the same path was later recreated, the stale public share re-exposed
the new content. Normalize the path before the prefix query so the exact share
and its descendants are both removed. Adds a regression test.
2026-06-27 09:08:57 +02:00

105 lines
2.3 KiB
Go

package bolt
import (
"errors"
"strings"
"github.com/asdine/storm/v3"
"github.com/asdine/storm/v3/q"
fberrors "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/share"
)
type shareBackend struct {
db *storm.DB
}
func (s shareBackend) All() ([]*share.Link, error) {
var v []*share.Link
err := s.db.All(&v)
if errors.Is(err, storm.ErrNotFound) {
return v, fberrors.ErrNotExist
}
return v, err
}
func (s shareBackend) FindByUserID(id uint) ([]*share.Link, error) {
var v []*share.Link
err := s.db.Select(q.Eq("UserID", id)).Find(&v)
if errors.Is(err, storm.ErrNotFound) {
return v, fberrors.ErrNotExist
}
return v, err
}
func (s shareBackend) GetByHash(hash string) (*share.Link, error) {
var v share.Link
err := s.db.One("Hash", hash, &v)
if errors.Is(err, storm.ErrNotFound) {
return nil, fberrors.ErrNotExist
}
return &v, err
}
func (s shareBackend) GetPermanent(path string, id uint) (*share.Link, error) {
var v share.Link
err := s.db.Select(q.Eq("Path", path), q.Eq("Expire", 0), q.Eq("UserID", id)).First(&v)
if errors.Is(err, storm.ErrNotFound) {
return nil, fberrors.ErrNotExist
}
return &v, err
}
func (s shareBackend) Gets(path string, id uint) ([]*share.Link, error) {
var v []*share.Link
err := s.db.Select(q.Eq("Path", path), q.Eq("UserID", id)).Find(&v)
if errors.Is(err, storm.ErrNotFound) {
return v, fberrors.ErrNotExist
}
return v, err
}
func (s shareBackend) Save(l *share.Link) error {
return s.db.Save(l)
}
func (s shareBackend) Delete(hash string) error {
err := s.db.DeleteStruct(&share.Link{Hash: hash})
if errors.Is(err, storm.ErrNotFound) {
return nil
}
return err
}
func (s shareBackend) DeleteWithPathPrefix(pathPrefix string, userID uint) error {
// Share paths are stored without a trailing slash
prefix := strings.TrimRight(pathPrefix, "/")
var links []share.Link
if err := s.db.Prefix("Path", prefix, &links); err != nil {
if errors.Is(err, storm.ErrNotFound) {
return nil
}
return err
}
var err error
for _, link := range links {
if link.UserID != userID {
continue
}
if link.Path != prefix && !strings.HasPrefix(link.Path, prefix+"/") {
continue
}
err = errors.Join(err, s.db.DeleteStruct(&share.Link{Hash: link.Hash}))
}
return err
}