Bulk action on selected users applies to ALL users (bulk/all endpoint sent instead of bulk)

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

  1. Open the Users page in the panel.
  2. Select a small number of users via checkboxes (e.g. 100 out of a large user base).
  3. Open the bulk actions drawer and apply an action (we used “Disable”).
  4. 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 — explicit userIds
  • POST /api/users/bulk/all/update — everyone

Two things would help independently of the root cause:

  1. Ensure the bulk actions drawer always sends the selected-IDs variant when a
    selection exists.
  2. 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.

  1. Actions/Update only on selected users
  2. Actions/Update all users

Most likely, you simply mixed up the buttons.