2 Commits

Author SHA1 Message Date
jared 3c7b3475e4 Fix command palette 'Unassigned Tickets' filter using wrong parameter
The filter action used ?assigned_to=none but DashboardController only
recognises 'unassigned' as the sentinel value for that filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 14:30:31 -04:00
jared 55c2d5c596 Fix visibility bypass in export and insecure cookie in preferences
api/export_tickets.php: getAllTickets() was called without $currentUser,
so visibility filtering was skipped — any authenticated user could export
all tickets including confidential/internal ones.

api/user_preferences.php: the single-preference setcookie() call was
missing httponly/secure flags (batch path had them correctly). Also cast
preference values to string before passing to setPreference(string).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 14:29:09 -04:00
3 changed files with 6 additions and 6 deletions
+2 -2
View File
@@ -72,8 +72,8 @@ try {
} }
} else { } else {
// Get all tickets with filters (no pagination for export) // Get all tickets with filters (no pagination for export)
// getAllTickets already applies visibility filtering via getVisibilityFilter // Pass $currentUser so visibility filtering is applied correctly
$result = $ticketModel->getAllTickets(1, 10000, $status, 'created_at', 'desc', $category, $type, $search); $result = $ticketModel->getAllTickets(1, 10000, $status, 'created_at', 'desc', $category, $type, $search, [], $currentUser);
$tickets = $result['tickets']; $tickets = $result['tickets'];
} }
+3 -3
View File
@@ -43,7 +43,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
foreach ($data['preferences'] as $key => $value) { foreach ($data['preferences'] as $key => $value) {
$key = trim($key); $key = trim($key);
if (!in_array($key, $validKeys)) continue; if (!in_array($key, $validKeys)) continue;
$prefsModel->setPreference($userId, $key, $value); $prefsModel->setPreference($userId, $key, (string)$value);
if ($key === 'rows_per_page') { if ($key === 'rows_per_page') {
setcookie('ticketsPerPage', $value, ['expires' => time() + (86400 * 365), 'path' => '/', 'httponly' => true, 'secure' => true, 'samesite' => 'Lax']); setcookie('ticketsPerPage', $value, ['expires' => time() + (86400 * 365), 'path' => '/', 'httponly' => true, 'secure' => true, 'samesite' => 'Lax']);
} }
@@ -73,11 +73,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
try { try {
$success = $prefsModel->setPreference($userId, $key, $value); $success = $prefsModel->setPreference($userId, $key, (string)$value);
// Also update cookie for rows_per_page for backwards compatibility // Also update cookie for rows_per_page for backwards compatibility
if ($key === 'rows_per_page') { if ($key === 'rows_per_page') {
setcookie('ticketsPerPage', $value, time() + (86400 * 365), '/'); setcookie('ticketsPerPage', (string)$value, ['expires' => time() + (86400 * 365), 'path' => '/', 'httponly' => true, 'secure' => true, 'samesite' => 'Lax']);
} }
apiRespond(['success' => $success]); apiRespond(['success' => $success]);
+1 -1
View File
@@ -233,7 +233,7 @@ $_lt_assetVer = $GLOBALS['config']['ASSET_VERSION'] ?? '20260329';
{ id: 'nav-dashboard', label: 'Dashboard', icon: '⌂', group: 'Navigate', action: function(){ location.href = '/'; } }, { id: 'nav-dashboard', label: 'Dashboard', icon: '⌂', group: 'Navigate', action: function(){ location.href = '/'; } },
{ id: 'nav-new-ticket', label: 'New Ticket', icon: '+', group: 'Navigate', kbd: 'N', action: function(){ location.href = '/create'; } }, { id: 'nav-new-ticket', label: 'New Ticket', icon: '+', group: 'Navigate', kbd: 'N', action: function(){ location.href = '/create'; } },
{ id: 'filter-mine', label: 'My Open Tickets', icon: '◈', group: 'Filter', action: function(){ location.href = '/?assigned_to=me&status=Open,In+Progress,Pending'; } }, { id: 'filter-mine', label: 'My Open Tickets', icon: '◈', group: 'Filter', action: function(){ location.href = '/?assigned_to=me&status=Open,In+Progress,Pending'; } },
{ id: 'filter-unassigned', label: 'Unassigned Tickets', icon: '◌', group: 'Filter', action: function(){ location.href = '/?assigned_to=none'; } }, { id: 'filter-unassigned', label: 'Unassigned Tickets', icon: '◌', group: 'Filter', action: function(){ location.href = '/?assigned_to=unassigned'; } },
{ id: 'filter-critical', label: 'P1 Critical Tickets', icon: '!', group: 'Filter', action: function(){ location.href = '/?priority=1'; } }, { id: 'filter-critical', label: 'P1 Critical Tickets', icon: '!', group: 'Filter', action: function(){ location.href = '/?priority=1'; } },
]; ];
if (isAdmin) { if (isAdmin) {