fix(auth): block privilege escalation via role/user management
The dynamic-RBAC management routes are themselves grantable (role:* and user:*), so a non-admin holding them could self-escalate: edit their own role to add a permission they lack, mint a privileged role, assign someone the admin role, or reset/delete a more-privileged account. Found by the commit security review (2× HIGH). Fix — enforce the RBAC invariant "you cannot grant beyond yourself": - roles.ts: role:create/update reject any permission not held by the caller (escalates()). An admin holds the full set, so it stays unrestricted. - users.ts: user:create/update reject assigning a role whose permissions exceed the caller's; update/password-reset/delete reject acting on a user whose current role exceeds the caller's (exceedsCaller()). The existing no-lockout + builtin-admin protections are unchanged. Verified: 10-assertion inject test — manager (role:* + user:* but no tariff:update, not admin) gets 403 on self-grant, minting a privileged role, assigning/resetting/deleting an admin; admin stays unrestricted; the manager can still create peers + in-scope roles (not over-blocked). Full build green. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -37,6 +37,12 @@ Authentication and authorization, kept **fully local** — a direct consequence
|
||||
**last user holding admin** — administration can never be locked out of the appliance.
|
||||
- `event:void` is a permission, NOT a ledger delete: the append-only signed chain is untouched; the
|
||||
permission only gates who may APPEND a void event (there is no void API route yet — forward seam).
|
||||
- **No privilege escalation through the RBAC system itself.** `role:create`/`role:update` and
|
||||
`user:create`/`user:update` are themselves grantable, so a non-admin could otherwise self-escalate.
|
||||
Guards (`routes/roles.ts`, `routes/users.ts`): a caller may only put permissions on a role that
|
||||
they *already hold*, and may only assign/modify users whose role is a SUBSET of the caller's own
|
||||
(so no minting a privileged role, handing out the admin role, or resetting/deleting a more-
|
||||
privileged account). An admin holds the full set, so it is unrestricted — the intended behaviour.
|
||||
|
||||
## Cookie session (browser auth)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user