Measured in passing while driving sys_user write doors for cloud#1989, on a real
walled boot of the cloud control plane (OS_MODE=cloud, posture isolated,
Organizations mounted) at framework pin 5b2ad1b41.
The measurement, with the control that gives it meaning
Requests below are a rank-and-file member's session cookie unless stated.
Everything was read back from the sqlite file with the server stopped.
POST /api/v1/auth/admin/definitely-not-a-route-1989 -> 200 {} <-- CONTROL: this path does not exist
POST /api/v1/auth/admin/update-user (member) -> 200 {} nothing landed
POST /api/v1/auth/admin/update-user (PLATFORM ADMIN) -> 200 {} nothing landed either
GET /api/v1/auth/admin/list-users (member) -> 200 {}
GET /api/v1/auth/admin/list-users (PLATFORM ADMIN) -> 200 {}
POST /api/v1/auth/admin/set-role (member) -> 200 {} role stayed null
POST /api/v1/auth/admin/ban-user (member) -> 403 {"success":false,"error":{"code":"PERMISSION_DENIED","message":"Admin role required"}}
POST /api/v1/auth/admin/set-user-password (member) -> 403 same envelope
The positive control that the auth surface itself is live:
POST /api/v1/auth/update-user as the member returned 200 {"status":true} and
the new name was present in the sqlite file afterwards.
The finding
Two things, and the first is the one that matters:
-
A path that does not exist answers 200 with an empty body. A caller
cannot tell "your write was accepted" from "there is no such route". Every
client-side success check — res.ok, status === 200, "no error thrown" —
reads a nonexistent admin endpoint as a completed admin action. This is the
silent-success shape; it should be a 404.
-
update-user, list-users and set-role under /api/v1/auth/admin/ are
indistinguishable from that nonexistent path on this composition, while
ban-user and set-user-password answer a real guarded 403. So part of the
better-auth admin family is reachable and part resolves to nothing, with no
way to tell from the wire which is which.
What this is NOT
Not a privilege leak. Nothing an ordinary member sent through these paths changed
any row — the store was read from the sqlite file with the server stopped and
every target row was byte-unchanged. The platform admin got the same vacuous 200,
which is what proves the endpoints are absent rather than silently permissive.
Why it still deserves a card
A vacuous 200 is exactly the answer that makes a future security probe conclude
"admitted" and file a false p0, or conclude "refused" and miss a real one — the
cloud#1989 measurement had to add a nonexistent-route control specifically to
tell them apart, which is a control no ordinary caller can run.
Filed with no assignee by the cloud#1989 measurement seat, which is
measurement-only. Step 1 for whoever takes it: reproduce on a framework-side boot
to confirm the mounting rather than the cloud composition is what decides it.
Measured in passing while driving
sys_userwrite doors for cloud#1989, on a realwalled boot of the cloud control plane (
OS_MODE=cloud, postureisolated,Organizationsmounted) at framework pin5b2ad1b41.The measurement, with the control that gives it meaning
Requests below are a rank-and-file member's session cookie unless stated.
Everything was read back from the sqlite file with the server stopped.
The positive control that the auth surface itself is live:
POST /api/v1/auth/update-useras the member returned200 {"status":true}andthe new
namewas present in the sqlite file afterwards.The finding
Two things, and the first is the one that matters:
A path that does not exist answers
200with an empty body. A callercannot tell "your write was accepted" from "there is no such route". Every
client-side success check —
res.ok,status === 200, "no error thrown" —reads a nonexistent admin endpoint as a completed admin action. This is the
silent-success shape; it should be a 404.
update-user,list-usersandset-roleunder/api/v1/auth/admin/areindistinguishable from that nonexistent path on this composition, while
ban-userandset-user-passwordanswer a real guarded 403. So part of thebetter-auth admin family is reachable and part resolves to nothing, with no
way to tell from the wire which is which.
What this is NOT
Not a privilege leak. Nothing an ordinary member sent through these paths changed
any row — the store was read from the sqlite file with the server stopped and
every target row was byte-unchanged. The platform admin got the same vacuous 200,
which is what proves the endpoints are absent rather than silently permissive.
Why it still deserves a card
A vacuous 200 is exactly the answer that makes a future security probe conclude
"admitted" and file a false p0, or conclude "refused" and miss a real one — the
cloud#1989 measurement had to add a nonexistent-route control specifically to
tell them apart, which is a control no ordinary caller can run.
Filed with no assignee by the cloud#1989 measurement seat, which is
measurement-only. Step 1 for whoever takes it: reproduce on a framework-side boot
to confirm the mounting rather than the cloud composition is what decides it.