server: anonymous LIST of users returns 403, not 200 with an empty array

Found while re-verifying the security posture against the public hostname now
that the server is internet-exposed. pb_hooks/main.pb.js covered bookcases,
shelves and books but never users, so an anonymous GET of the accounts
collection answered 200 with an empty array.

Nothing leaked: two real accounts exist and the declarative listRule filtered
both out, so no account, email or id was ever visible to an anonymous caller.
But "200 with []" is the exact wrong signal this hook exists to remove — some
clients read it as an allowed request — and the accounts collection is the last
place to leave it. Defensible while the server was localhost-only; not now.

Re-verified over the internet after restarting the service: books, shelves,
bookcases and users all 403 anonymous, self-registration 403, health 200.

Also re-verified that login still works, since this hook now runs on a
collection the app authenticates against: auth-with-password returns 200 with a
token, and an authenticated LIST of all four collections still returns 200. The
hook rejects unauthenticated list/search only, and auth-with-password is not a
list request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPpdG8VnRfS3KkisR3HUAE
This commit is contained in:
Spriteandclaude committed 2026-09-12 17:47:43 +00:00
1 parent 93546ed724
commit 0455794603
3 files changed
+42 -17

No files matched your search

+12 -6
View File
@@ -90,12 +90,18 @@ A `200` with a non-empty `items` array from any of those is a breach, and a
`200` with an empty one means the `pb_hooks` guard is not loaded — see the
quirk described above.
> **Known gap:** `GET /api/collections/users/records` currently answers
> **200 with an empty array** rather than 403. Nothing is disclosed — the
> `listRule` filters every row out, so an anonymous caller sees no accounts,
> no emails, no ids — but it is the same wrong-signal quirk as above, and
> `pb_hooks/main.pb.js` does not yet cover the `users` collection. Adding it
> to that hook's collection list closes it.
Check the accounts collection too — it is covered by the same hook:
```sh
curl -s -o /dev/null -w '%{http_code}\n' "$BASE/api/collections/users/records"
# -> 403
# ...but logging in must still work, which is the thing to re-check after any
# change to pb_hooks (the hook rejects anonymous LIST, not authentication):
curl -s -o /dev/null -w '%{http_code}\n' -X POST "$BASE/api/collections/users/auth-with-password" \
-H 'Content-Type: application/json' -d '{"identity":"you@example.com","password":"..."}'
# -> 200
```
## Where the live server runs