Directory delete removes share links by separator-unaware, cross-user path prefix, deleting other users' share records
low Fully Valid high confidence
Status
Fully Valid
Both the sibling over-match and the cross-user reachability (scope-relative Path + no UserID filter) are confirmed in code and the storm dependency source. Fully_valid; note cross-user share-link deletion could justify medium severity.
Repository / Component
Plain-English Description
When a user deletes a folder, the app also deletes share links whose stored path merely starts with the same text — including unrelated folders and links belonging to other users. Those users' share URLs silently stop working.
Description of the Underlying Issue
When a resource is deleted, resourceDeleteHandler (http/resource.go:103) calls shareBackend.DeleteWithPathPrefix(file.Path). In storage/bolt/share.go:79-90 this runs s.db.Prefix('Path', pathPrefix, &links) — a raw string prefix match with no separator boundary and no UserID filter. The passed prefix (file.Path) is scope-relative and not separator-terminated, so deleting '/foo' also matches '/foobar', '/foo-old', etc. Share link paths are stored as r.URL.Path scope-relative to the owner (http/share.go:157), so path strings collide across different users. The underlying storm Prefix (github.com/asdine/storm/v3@v3.2.1 finder.go:435) uses q.Re('^'+prefix) for unindexed fields and a byte-prefix index scan otherwise — both separator-unaware. As a result one user's directory delete removes share-link DB records belonging to other users whose stored Path shares the string prefix. Only the share.Link records are deleted; the underlying files are not.
Potential Attack
User A creates and then deletes a directory '/a' in their scope (DELETE /api/resources/a). DeleteWithPathPrefix('/a') matches every share link whose Path begins with '/a' — including User B's share for '/apple' (stored scope-relative as '/apple', no UserID scoping) — and deletes those records. No permission or ownership check intervenes because the query filters only on the Path prefix.
Outcomes of Potential Attack
Cross-user denial of service on sharing: other users' share URLs silently break (their share.Link records are deleted) with no notification. It also affects the deleting user's own unrelated sibling shares (e.g. '/foobar' when deleting '/foo'). File contents are unaffected; the damage is limited to share-link records.
Affected Scope
shareBackend.DeleteWithPathPrefix (storage/bolt/share.go:79-90) invoked by resourceDeleteHandler (http/resource.go:103)
Suggested Fix (plain english)
Only delete share links for the exact deleted folder and its true contents, and only for the user who owns them.
Suggested Fix (detailed)
Change the match in storage/bolt/share.go to Path == prefix OR Path HasPrefix(prefix + '/') so only the deleted path and its genuine descendants match (eliminating '/foobar'-style sibling over-match), and scope the deletion by the deleting user's UserID so it cannot touch other users' records. Verify by creating shares for '/a' and '/apple' (as different users) and confirming that deleting '/a' leaves '/apple' intact. Match Path == prefix OR Path HasPrefix(prefix + '/'), and scope the deletion by the deleting user's UserID.
Validation
Both the sibling over-match and the cross-user reachability (scope-relative Path + no UserID filter) are confirmed in code and the storm dependency source. Fully_valid; note cross-user share-link deletion could justify medium severity.
Full Evidence
storage/bolt/share.go:81 s.db.Prefix('Path', pathPrefix, &links) — raw prefix match, no separator boundary, no UserID filterhttp/resource.go:103 DeleteWithPathPrefix(file.Path) passes a non-separator-terminated, scope-relative path
http/share.go:157 share.Link.Path is stored as r.URL.Path (scope-relative to the owner), so path strings collide across users
dependency github.com/asdine/storm/v3@v3.2.1 finder.go:435 uses q.Re('^'+prefix) for unindexed fields and a byte-prefix index scan otherwise — both are separator-unaware string prefixesProven fact: The prefix match is separator-unaware: deleting '/foo' also matches '/foobar', '/foo-old', etc.
Proven fact: There is no UserID scoping, and Paths are stored scope-relative, so one user's directory delete removes other users' share-link records whose Path shares the string prefix
Proven fact: Only the share.Link DB records are deleted (not the underlying files)
Unvalidated fact: Frequency of real-world sibling/cross-user path-prefix collisions is deployment-dependent (though common scope-relative names like '/documents' make collisions plausible)
storage/bolt/share.go:81 — s.db.Prefix('Path', pathPrefix, &links): raw prefix match, no separator boundary, no UserID filterhttp/resource.go:103 — DeleteWithPathPrefix(file.Path) passes a non-separator-terminated, scope-relative path
http/share.go:157 — share.Link.Path is stored as r.URL.Path (scope-relative to the owner), so path strings collide across users
dependency github.com/asdine/storm/v3@v3.2.1 finder.go:435 — Prefix uses q.Re('^'+prefix) for unindexed fields and a byte-prefix index scan otherwise; both are separator-unaware string prefixesProven fact: There is no UserID scoping and Paths are stored scope-relative, so one user's directory delete removes other users' share-link records whose Path shares the string prefix.
Proven fact: Only the share.Link DB records are deleted (not the underlying files).
Unvalidated fact: Frequency of real-world sibling/cross-user path-prefix collisions is deployment-dependent, though common scope-relative names like '/documents' make collisions plausible.
http/resource.go:103 d.store.Share.DeleteWithPathPrefix(file.Path) passes a non-separator-terminated path
storage/bolt/share.go:79-90 s.db.Prefix("Path", pathPrefix, &links) does raw string-prefix match with no path-boundary normalization and no UserID scopingshare/storage.go:140-142 wrapper forwards directly to backend