Summary
Selecting a subset of users in the Users table and applying a bulk action causes the
panel to call the “all users” endpoint instead of the “selected users” one. The
action is applied to the entire database, silently ignoring the selection.
In our case an operator selected ~100 users and clicked “Disable”. Every user in the
panel was disabled instead — the entire user base (tens of thousands of accounts),
in a single request. All customers lost service until we bulk re-enabled them.
Affected versions
Observed on 3.3.2. Reviewing the frontend changelog for 3.4.0 → 3.4.3 I found no
related fix, so this appears to still be present in 3.4.3. The last relevant commit
I could find is fix: reset row selection before opening bulk action drawer (Nov 2025).
Steps to reproduce
- Open the Users page in the panel.
- Select a small number of users via checkboxes (e.g. 100 out of a large user base).
- Open the bulk actions drawer and apply an action (we used “Disable”).
- Observe the outgoing request in DevTools → Network.
Expected
POST /api/users/bulk/update with an explicit userIds array containing only the
selected users.
Actual
POST /api/users/bulk/all/update — the “apply to all users” variant. The selection is
not sent and not honoured; the action hits every user in the database.
Evidence
Reverse-proxy access log entry, matching the incident to the second (IP and host redacted):
<redacted> - - \[23/Aug/2026:21:04:33 +0000\] "POST /api/users/bulk/all/update HTTP/2.0" 202 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36"
Only one request was made. Database updated_at timestamps confirm the whole user
table was modified within that same minute.
Impact
Critical for any production deployment. A routine moderation action — disabling a
handful of abusers — takes the entire service offline. There is no confirmation step
that distinguishes “apply to 100 selected” from “apply to all”, and no way to undo it:
recovering requires re-enabling every user, which also loses the previous per-user
state (users that were legitimately disabled before are indistinguishable afterwards).
Suggested direction
The two endpoint families are easy to confuse and are only one path segment apart:
POST /api/users/bulk/update— explicituserIdsPOST /api/users/bulk/all/update— everyone
Two things would help independently of the root cause:
- Ensure the bulk actions drawer always sends the selected-IDs variant when a
selection exists. - Require an explicit, separate confirmation for the
bulk/all/*family (e.g. typing
the affected count), since its blast radius is the whole database.
As a stopgap we now return 403 for /api/users/bulk/all at our reverse proxy, which
makes the destructive variant unreachable while leaving selected-user bulk actions
working normally.
