Filed unassigned by the #9714 dev (session session_01PnJHU45vPJj5UQrxe946Bx) as an out_of_scope_findings item. Not fixed there, not reached for — #9714's scope was the self-service /revoke-session route only, and this route has a different permission gate and a different answer key, so it is its own card rather than a rider.
The defect
Read from the installed vendor dist on 2026-08-19: better-auth 1.7.1 (dist/plugins/admin/routes.mjs), the POST /admin/revoke-user-session handler runs, after its session: ["revoke"] permission check:
awaitctx.context.internalAdapter.deleteSession(ctx.body.sessionToken);returnctx.json({success: true});There is no match check at all — deleteSession on a token matching zero rows deletes nothing, and the endpoint answers 200 { success: true } unconditionally. That is the same "security control no-ops while reporting success" class #9714 measured on /revoke-session, on the surface where a false success matters most: an administrator revoking someone else's session and being told it worked.
Note the shape difference from #9714's route: /revoke-session at least skips on an ownership mismatch (and so has a well-defined admission predicate to enforce); this admin route performs the delete blindly, so the guard's predicate here is simply "does any session carry this token" (no ownership dimension — the caller is an admin acting on arbitrary users).
Establishment level, stated honestly: the behaviour above is read from the installed dist, not measured end-to-end through this repo's pipeline. #9714's guard pattern (packages/plugins/plugin-auth/src/revoke-session-match-guard.ts, a hooks.before admission gate answering 404 RESOURCE_NOT_FOUND per ADR-0112) is the natural template, but the admin route's interaction with the #7732 tombstone rules (/admin/revoke-user-session is in INTERACTIVE_REVOKE_REASON, reason admin) should be re-verified behaviourally first, exactly as #9714 required.
#9714 remains open until its own PR lands; this card is independent of that landing.
Generated by Claude Code
Filed unassigned by the #9714 dev (session
session_01PnJHU45vPJj5UQrxe946Bx) as anout_of_scope_findingsitem. Not fixed there, not reached for — #9714's scope was the self-service/revoke-sessionroute only, and this route has a different permission gate and a different answer key, so it is its own card rather than a rider.The defect
Read from the installed vendor dist on 2026-08-19: better-auth 1.7.1 (
dist/plugins/admin/routes.mjs), thePOST /admin/revoke-user-sessionhandler runs, after itssession: ["revoke"]permission check:There is no match check at all —
deleteSessionon a token matching zero rows deletes nothing, and the endpoint answers200 { success: true }unconditionally. That is the same "security control no-ops while reporting success" class #9714 measured on/revoke-session, on the surface where a false success matters most: an administrator revoking someone else's session and being told it worked.Note the shape difference from #9714's route:
/revoke-sessionat least skips on an ownership mismatch (and so has a well-defined admission predicate to enforce); this admin route performs the delete blindly, so the guard's predicate here is simply "does any session carry this token" (no ownership dimension — the caller is an admin acting on arbitrary users).Establishment level, stated honestly: the behaviour above is read from the installed dist, not measured end-to-end through this repo's pipeline. #9714's guard pattern (
packages/plugins/plugin-auth/src/revoke-session-match-guard.ts, ahooks.beforeadmission gate answering 404RESOURCE_NOT_FOUNDper ADR-0112) is the natural template, but the admin route's interaction with the #7732 tombstone rules (/admin/revoke-user-sessionis inINTERACTIVE_REVOKE_REASON, reasonadmin) should be re-verified behaviourally first, exactly as #9714 required.#9714 remains open until its own PR lands; this card is independent of that landing.
Generated by Claude Code