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:
2026-03-28 13:41:22 -04:00
parent e2c23d0405
commit 82aa4bf5de
2 changed files with 62 additions and 19 deletions
+52 -14
View File
@@ -73,17 +73,36 @@ try {
case 'POST':
$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
(template_name, title_template, description_template, category, type, default_priority, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param('sssssii',
$data['template_name'],
$data['title_template'],
$data['description_template'],
$data['category'],
$data['type'],
$data['default_priority'] ?? 4,
$data['is_active'] ?? 1
$templateName,
$titleTemplate,
$description,
$category,
$type,
$priority,
$isActive
);
if ($stmt->execute()) {
@@ -103,18 +122,37 @@ try {
$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
template_name = ?, title_template = ?, description_template = ?,
category = ?, type = ?, default_priority = ?, is_active = ?
WHERE template_id = ?");
$stmt->bind_param('sssssiii',
$data['template_name'],
$data['title_template'],
$data['description_template'],
$data['category'],
$data['type'],
$data['default_priority'] ?? 4,
$data['is_active'] ?? 1,
$templateName,
$titleTemplate,
$description,
$category,
$type,
$priority,
$isActive,
$id
);