Files
pad/internal/server
xarmian 22ac55929a fix(server): gate ID-keyed grant/share-link handlers on target visibility (BUG-1923) (#798)
* Gate ID-keyed grant/share-link delete and view-history handlers on target visibility

handleDeleteCollectionGrant, handleDeleteItemGrant, handleDeleteShareLink,
and handleShareLinkViews operated on a grant/link ID directly with only a
requireMinRole(owner) check — a restricted owner who knew an ID could
revoke a grant or share link (or read its view-history) on a resource
hidden from them by collection_access="specific". Unlike their
slug-resolving create/list siblings (fixed in BUG-1920), these handlers
never resolved the parent item/collection, so the visibility gate never
ran.

Resolve the grant/link's parent before acting: collection-scoped records
go through the strict requireCollectionFullyVisible (no item-grant
promotion, matching the minting/listing gates), item-scoped records
through requireItemVisible. Item lookups use GetItemIncludeDeleted so a
grant/link on a trashed-but-visible item stays revocable, matching
handleListItemGrants' existing ResolveItemIncludeDeleted precedent.

Fixes BUG-1923.

* Fix soft-deleted-collection regression in BUG-1923's visibility gates

Codex round 3 found that the BUG-1923 fix used GetCollection to resolve
the parent collection for collection-scoped grant/share-link operations.
GetCollection filters deleted_at IS NULL, but DeleteCollection soft-deletes
and does NOT cascade-delete collection_grants or share_links — so the
moment a collection was archived, its grants/share-links became
permanently un-revocable and un-inspectable via the API (parent resolution
404'd before the visibility check even ran).

Switch both call sites to the existing GetCollectionAnyState store method
(no deleted_at filter; already used by the open-children guard for the
same reason) instead of adding a new near-duplicate getter. Applied
consistently to both handleDeleteShareLink and handleShareLinkViews via
the shared requireShareLinkTargetVisible helper, so view-history stays
readable for revocation decisions on an archived collection's link, not
just the delete path.

Confirmed via store trace: an unrestricted owner (visibleCollectionIDs ==
nil) is unaffected either way and is the main regression case, now fixed.
A restricted owner's member_collection_access-derived visibility also
survives the parent's soft-delete, since that lookup reads the raw table
with no deleted_at join.
2026-07-03 19:41:57 -04:00
..