Fix loose comparisons, missing response codes, and session handling
- ticket.js: escape dependency_id with lt.escHtml() in data attribute - assign_ticket.php: strict (int) cast for ticket_id (> 0 check), authorization comparisons, and add missing http_response_code(400) on invalid user ID - TicketView.php: strict (int) cast for priority select, assigned_to select, and comment ownership check - CommentModel.php: strict (int) cast for parent_comment_id thread comparison - UserModel.php: strict (int) cast for is_admin check - export_tickets.php: conditional session_start() to avoid double-start warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,10 +12,10 @@ if (!is_array($data)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ticketId = isset($data['ticket_id']) ? (int)$data['ticket_id'] : null;
|
$ticketId = isset($data['ticket_id']) ? (int)$data['ticket_id'] : 0;
|
||||||
$assignedTo = $data['assigned_to'] ?? null;
|
$assignedTo = $data['assigned_to'] ?? null;
|
||||||
|
|
||||||
if (!$ticketId) {
|
if ($ticketId <= 0) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['success' => false, 'error' => 'Ticket ID required']);
|
echo json_encode(['success' => false, 'error' => 'Ticket ID required']);
|
||||||
exit;
|
exit;
|
||||||
@@ -34,7 +34,7 @@ if (!$ticket || !$ticketModel->canUserAccessTicket($ticket, $currentUser)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Authorization: only admins or the ticket creator/assignee can reassign
|
// Authorization: only admins or the ticket creator/assignee can reassign
|
||||||
if (!$isAdmin && $ticket['created_by'] !== $userId && $ticket['assigned_to'] !== $userId) {
|
if (!$isAdmin && (int)$ticket['created_by'] !== (int)$userId && (int)$ticket['assigned_to'] !== (int)$userId) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['success' => false, 'error' => 'Permission denied']);
|
echo json_encode(['success' => false, 'error' => 'Permission denied']);
|
||||||
exit;
|
exit;
|
||||||
@@ -51,6 +51,7 @@ if ($assignedTo === null || $assignedTo === '') {
|
|||||||
$assignedTo = (int)$assignedTo;
|
$assignedTo = (int)$assignedTo;
|
||||||
$targetUser = $userModel->getUserById($assignedTo);
|
$targetUser = $userModel->getUserById($assignedTo);
|
||||||
if (!$targetUser) {
|
if (!$targetUser) {
|
||||||
|
http_response_code(400);
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid user ID']);
|
echo json_encode(['success' => false, 'error' => 'Invalid user ID']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ try {
|
|||||||
require_once dirname(__DIR__) . '/models/TicketModel.php';
|
require_once dirname(__DIR__) . '/models/TicketModel.php';
|
||||||
|
|
||||||
// Check authentication via session
|
// Check authentication via session
|
||||||
session_start();
|
if (session_status() === PHP_SESSION_NONE) { session_start(); }
|
||||||
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) {
|
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
|
|||||||
+1
-1
@@ -512,7 +512,7 @@ function renderDependencies(dependencies) {
|
|||||||
<span class="dependency-title">${lt.escHtml(dep.title)}</span>
|
<span class="dependency-title">${lt.escHtml(dep.title)}</span>
|
||||||
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
|
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
|
||||||
</div>
|
</div>
|
||||||
<button data-action="remove-dependency" data-dependency-id="${dep.dependency_id}" class="lt-btn lt-btn-sm">REMOVE</button>
|
<button data-action="remove-dependency" data-dependency-id="${lt.escHtml(String(dep.dependency_id))}" class="lt-btn lt-btn-sm">REMOVE</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class CommentModel {
|
|||||||
private function buildCommentThread($comment, &$allComments) {
|
private function buildCommentThread($comment, &$allComments) {
|
||||||
$comment['replies'] = [];
|
$comment['replies'] = [];
|
||||||
foreach ($allComments as $c) {
|
foreach ($allComments as $c) {
|
||||||
if ($c['parent_comment_id'] == $comment['comment_id']
|
if ((int)$c['parent_comment_id'] === (int)$comment['comment_id']
|
||||||
&& isset($allComments[$c['comment_id']])) {
|
&& isset($allComments[$c['comment_id']])) {
|
||||||
$comment['replies'][] = $this->buildCommentThread($c, $allComments);
|
$comment['replies'][] = $this->buildCommentThread($c, $allComments);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ class UserModel {
|
|||||||
* @return bool True if user is admin
|
* @return bool True if user is admin
|
||||||
*/
|
*/
|
||||||
public function isAdmin(array $user): bool {
|
public function isAdmin(array $user): bool {
|
||||||
return isset($user['is_admin']) && $user['is_admin'] == 1;
|
return isset($user['is_admin']) && (int)$user['is_admin'] === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ include __DIR__ . '/layout_header.php';
|
|||||||
<span class="lt-kv-value">
|
<span class="lt-kv-value">
|
||||||
<select id="prioritySelect" class="lt-select lt-select-sm editable-metadata" disabled aria-label="Priority">
|
<select id="prioritySelect" class="lt-select lt-select-sm editable-metadata" disabled aria-label="Priority">
|
||||||
<?php foreach ([1=>'P1 - Critical',2=>'P2 - High',3=>'P3 - Medium',4=>'P4 - Low',5=>'P5 - Minimal'] as $v=>$l): ?>
|
<?php foreach ([1=>'P1 - Critical',2=>'P2 - High',3=>'P3 - Medium',4=>'P4 - Low',5=>'P5 - Minimal'] as $v=>$l): ?>
|
||||||
<option value="<?= $v ?>" <?= $ticket['priority'] == $v ? 'selected' : '' ?>><?= $l ?></option>
|
<option value="<?= $v ?>" <?= (int)$ticket['priority'] === $v ? 'selected' : '' ?>><?= $l ?></option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
@@ -183,7 +183,7 @@ include __DIR__ . '/layout_header.php';
|
|||||||
<option value="">Unassigned</option>
|
<option value="">Unassigned</option>
|
||||||
<?php foreach ($allUsers as $u): ?>
|
<?php foreach ($allUsers as $u): ?>
|
||||||
<option value="<?= (int)$u['user_id'] ?>"
|
<option value="<?= (int)$u['user_id'] ?>"
|
||||||
<?= ($ticket['assigned_to'] == $u['user_id']) ? 'selected' : '' ?>>
|
<?= ((int)$ticket['assigned_to'] === (int)$u['user_id']) ? 'selected' : '' ?>>
|
||||||
<?= htmlspecialchars($u['display_name'] ?? $u['username']) ?>
|
<?= htmlspecialchars($u['display_name'] ?? $u['username']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
@@ -361,7 +361,7 @@ include __DIR__ . '/layout_header.php';
|
|||||||
function renderComment(array $comment, ?int $currentUserId, bool $isAdmin, int $depth = 0): void {
|
function renderComment(array $comment, ?int $currentUserId, bool $isAdmin, int $depth = 0): void {
|
||||||
$displayName = $comment['display_name_formatted'] ?? $comment['user_name'] ?? 'Unknown User';
|
$displayName = $comment['display_name_formatted'] ?? $comment['user_name'] ?? 'Unknown User';
|
||||||
$commentId = (int)$comment['comment_id'];
|
$commentId = (int)$comment['comment_id'];
|
||||||
$isOwner = ($comment['user_id'] == $currentUserId);
|
$isOwner = ((int)$comment['user_id'] === (int)$currentUserId);
|
||||||
$canModify = $isOwner || $isAdmin;
|
$canModify = $isOwner || $isAdmin;
|
||||||
$markdownEnabled = (bool)($comment['markdown_enabled'] ?? false);
|
$markdownEnabled = (bool)($comment['markdown_enabled'] ?? false);
|
||||||
$threadDepth = (int)($comment['thread_depth'] ?? $depth);
|
$threadDepth = (int)($comment['thread_depth'] ?? $depth);
|
||||||
|
|||||||
Reference in New Issue
Block a user