Cap get_users.php result set as defense-in-depth (#42)

api/get_users.php returned every user's user_id/username/display_name
to any authenticated session with no pagination or limit — needed for
mention/assignment typeahead, but a blanket enumeration a compromised
low-privilege session could scrape in one call. Added a LIMIT 500;
every caller already only uses this for typeahead/dropdown filtering,
never a literal full roster, so this doesn't change behavior for any
real deployment size while bounding the response.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGDKHiU5RJdo3dqQUDow3X
This commit is contained in:
2026-09-08 11:53:36 -04:00
co-authored by Claude Sonnet 5
parent 92aea89b74
commit 0d6b08f5d2
+4 -2
View File
@@ -8,8 +8,10 @@
require_once __DIR__ . '/bootstrap.php'; require_once __DIR__ . '/bootstrap.php';
try { try {
// Get all users for mentions/assignment // Get all users for mentions/assignment. Capped as defense-in-depth against
$result = Database::query("SELECT user_id, username, display_name FROM users ORDER BY display_name, username"); // a single call scraping an unbounded user list — every caller only needs
// this for typeahead/dropdown filtering, never a literal full roster.
$result = Database::query("SELECT user_id, username, display_name FROM users ORDER BY display_name, username LIMIT 500");
if (!$result) { if (!$result) {
throw new Exception("Failed to query users"); throw new Exception("Failed to query users");