Found by the ADR-0090 D10 adversarial security review. Same root cause as the settings-routes fix (#2848) — the raw-app route registration trusts identity headers.
What
packages/plugins/plugin-sharing/src/share-link-routes.ts:~37-46 uses a defaultContext that reads x-user-id / x-tenant-id from request headers, wired without a verified contextFromRequest (sharing-plugin.ts:~291). Registered on the raw Hono app with no auth middleware.
Impact
createLink stamps created_by: context.userId → a client can forge attribution to any user, and create links for any record of a publicSharing-enabled object (existence checked with SYSTEM_CTX, so the caller's own access is never verified).
GET /api/v1/share-links with X-User-Id: <victim> → listLinks(createdBy: victim) enumerates another user's share-link tokens, which then resolve records via the public /:token/resolve endpoint under SYSTEM_CTX (RLS bypassed).
DELETE → revoke arbitrary users' links.
A separate, correctly-verified share-link handler exists in the dispatcher (http-dispatcher.ts:~3879, uses context.executionContext); the raw-app header-trusted route shadows it.
Fix direction
Same pattern as #2848: derive identity from the verified resolveAuthzContext (session/API-key/OAuth) instead of headers; make the default secure; verify the caller's own access to the record before createLink. Consider removing the raw-app route in favour of the verified dispatcher handler.
Refs
Found by the ADR-0090 D10 adversarial security review. Same root cause as the settings-routes fix (#2848) — the raw-app route registration trusts identity headers.
What
packages/plugins/plugin-sharing/src/share-link-routes.ts:~37-46uses adefaultContextthat readsx-user-id/x-tenant-idfrom request headers, wired without a verifiedcontextFromRequest(sharing-plugin.ts:~291). Registered on the raw Hono app with no auth middleware.Impact
createLinkstampscreated_by: context.userId→ a client can forge attribution to any user, and create links for any record of apublicSharing-enabled object (existence checked withSYSTEM_CTX, so the caller's own access is never verified).GET /api/v1/share-linkswithX-User-Id: <victim>→listLinks(createdBy: victim)enumerates another user's share-link tokens, which then resolve records via the public/:token/resolveendpoint underSYSTEM_CTX(RLS bypassed).DELETE→ revoke arbitrary users' links.A separate, correctly-verified share-link handler exists in the dispatcher (
http-dispatcher.ts:~3879, usescontext.executionContext); the raw-app header-trusted route shadows it.Fix direction
Same pattern as #2848: derive identity from the verified
resolveAuthzContext(session/API-key/OAuth) instead of headers; make the default secure; verify the caller's own access to the record beforecreateLink. Consider removing the raw-app route in favour of the verified dispatcher handler.Refs
packages/plugins/plugin-sharing/src/share-link-routes.ts:~37-46,sharing-plugin.ts:~291packages/plugins/plugin-sharing/src/share-link-service.ts:~197-278