Harden attachment deletion and template CRUD validation
- delete_attachment.php: add realpath() path traversal check before unlink() — mirrors the defense-in-depth already in download_attachment.php; also cast ticket_id to int when building the path - manage_templates.php: add input validation to POST and PUT handlers: required field checks, max length caps (name 100, title 255, desc 64KB), allowlist validation for category/type, priority clamped to 1-5 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,12 +80,17 @@ try {
|
|||||||
ResponseHelper::forbidden('You do not have permission to delete this attachment');
|
ResponseHelper::forbidden('You do not have permission to delete this attachment');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the file
|
// Delete the file — use realpath() to prevent path traversal
|
||||||
$uploadDir = $GLOBALS['config']['UPLOAD_DIR'] ?? dirname(__DIR__) . '/uploads';
|
$uploadDir = realpath($GLOBALS['config']['UPLOAD_DIR'] ?? dirname(__DIR__) . '/uploads');
|
||||||
$filePath = $uploadDir . '/' . $attachment['ticket_id'] . '/' . $attachment['filename'];
|
$filePath = $uploadDir . '/' . (int)$attachment['ticket_id'] . '/' . $attachment['filename'];
|
||||||
|
$realPath = realpath($filePath);
|
||||||
|
|
||||||
if (file_exists($filePath)) {
|
if ($realPath !== false) {
|
||||||
if (!unlink($filePath)) {
|
// Ensure the resolved path is still inside the upload directory
|
||||||
|
if (strncmp($realPath, $uploadDir . DIRECTORY_SEPARATOR, strlen($uploadDir) + 1) !== 0) {
|
||||||
|
ResponseHelper::forbidden('Access denied');
|
||||||
|
}
|
||||||
|
if (!unlink($realPath)) {
|
||||||
ResponseHelper::serverError('Failed to delete file');
|
ResponseHelper::serverError('Failed to delete file');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-14
@@ -73,17 +73,36 @@ try {
|
|||||||
case 'POST':
|
case 'POST':
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
// Validate required fields and lengths
|
||||||
|
$templateName = trim($data['template_name'] ?? '');
|
||||||
|
$titleTemplate = trim($data['title_template'] ?? '');
|
||||||
|
if (!$templateName || mb_strlen($templateName) > 100) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Template name is required (max 100 chars)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!$titleTemplate || mb_strlen($titleTemplate) > 255) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Title template is required (max 255 chars)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$allowedCategories = ['General','Hardware','Software','Network','Security'];
|
||||||
|
$allowedTypes = ['Issue','Maintenance','Install','Task','Upgrade','Problem'];
|
||||||
|
$category = in_array($data['category'] ?? '', $allowedCategories) ? $data['category'] : 'General';
|
||||||
|
$type = in_array($data['type'] ?? '', $allowedTypes) ? $data['type'] : 'Issue';
|
||||||
|
$priority = max(1, min(5, (int)($data['default_priority'] ?? 4)));
|
||||||
|
$isActive = $data['is_active'] ? 1 : 0;
|
||||||
|
$description = mb_substr($data['description_template'] ?? '', 0, 65535);
|
||||||
|
|
||||||
$stmt = $conn->prepare("INSERT INTO ticket_templates
|
$stmt = $conn->prepare("INSERT INTO ticket_templates
|
||||||
(template_name, title_template, description_template, category, type, default_priority, is_active)
|
(template_name, title_template, description_template, category, type, default_priority, is_active)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)");
|
VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||||
$stmt->bind_param('sssssii',
|
$stmt->bind_param('sssssii',
|
||||||
$data['template_name'],
|
$templateName,
|
||||||
$data['title_template'],
|
$titleTemplate,
|
||||||
$data['description_template'],
|
$description,
|
||||||
$data['category'],
|
$category,
|
||||||
$data['type'],
|
$type,
|
||||||
$data['default_priority'] ?? 4,
|
$priority,
|
||||||
$data['is_active'] ?? 1
|
$isActive
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
@@ -103,18 +122,37 @@ try {
|
|||||||
|
|
||||||
$data = json_decode(file_get_contents('php://input'), true);
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
// Validate required fields and lengths
|
||||||
|
$templateName = trim($data['template_name'] ?? '');
|
||||||
|
$titleTemplate = trim($data['title_template'] ?? '');
|
||||||
|
if (!$templateName || mb_strlen($templateName) > 100) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Template name is required (max 100 chars)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!$titleTemplate || mb_strlen($titleTemplate) > 255) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Title template is required (max 255 chars)']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$allowedCategories = ['General','Hardware','Software','Network','Security'];
|
||||||
|
$allowedTypes = ['Issue','Maintenance','Install','Task','Upgrade','Problem'];
|
||||||
|
$category = in_array($data['category'] ?? '', $allowedCategories) ? $data['category'] : 'General';
|
||||||
|
$type = in_array($data['type'] ?? '', $allowedTypes) ? $data['type'] : 'Issue';
|
||||||
|
$priority = max(1, min(5, (int)($data['default_priority'] ?? 4)));
|
||||||
|
$isActive = $data['is_active'] ? 1 : 0;
|
||||||
|
$description = mb_substr($data['description_template'] ?? '', 0, 65535);
|
||||||
|
|
||||||
$stmt = $conn->prepare("UPDATE ticket_templates SET
|
$stmt = $conn->prepare("UPDATE ticket_templates SET
|
||||||
template_name = ?, title_template = ?, description_template = ?,
|
template_name = ?, title_template = ?, description_template = ?,
|
||||||
category = ?, type = ?, default_priority = ?, is_active = ?
|
category = ?, type = ?, default_priority = ?, is_active = ?
|
||||||
WHERE template_id = ?");
|
WHERE template_id = ?");
|
||||||
$stmt->bind_param('sssssiii',
|
$stmt->bind_param('sssssiii',
|
||||||
$data['template_name'],
|
$templateName,
|
||||||
$data['title_template'],
|
$titleTemplate,
|
||||||
$data['description_template'],
|
$description,
|
||||||
$data['category'],
|
$category,
|
||||||
$data['type'],
|
$type,
|
||||||
$data['default_priority'] ?? 4,
|
$priority,
|
||||||
$data['is_active'] ?? 1,
|
$isActive,
|
||||||
$id
|
$id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user