Files
breadcrumb-the-shire/modules/helpdesk/pages/helpdesk/ticket-file-data().php
fs f974ca4fd4 fix(helpdesk): add security headers and size limit to file proxy
- X-Content-Type-Options: nosniff prevents MIME sniffing
- Content-Security-Policy: sandbox prevents script execution
- 20 MB size limit on decoded file data (413 on exceed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 22:53:46 +02:00

63 lines
1.5 KiB
PHP

<?php
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
use MintyPHP\Module\Helpdesk\Service\BcSoapGateway;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin();
Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS);
$request = requestInput();
if ($request->method() !== 'GET') {
http_response_code(405);
Router::json(['ok' => false, 'error' => 'method_not_allowed']);
return;
}
$entryNo = (int) $request->query('entryNo', '0');
$filename = trim((string) $request->query('filename', ''));
if ($entryNo <= 0) {
http_response_code(400);
Router::json(['ok' => false, 'error' => 'missing_entry_no']);
return;
}
$soapGateway = app(BcSoapGateway::class);
try {
$result = $soapGateway->getTicketFile($entryNo);
} catch (\Throwable) {
http_response_code(502);
Router::json(['ok' => false, 'error' => 'Failed to load file']);
return;
}
if (!$result['ok'] || !isset($result['data'])) {
http_response_code(404);
Router::json(['ok' => false, 'error' => $result['error'] ?? 'File not found']);
return;
}
$data = $result['data'];
$maxFileSize = 20 * 1024 * 1024; // 20 MB
if (strlen($data) > $maxFileSize) {
http_response_code(413);
Router::json(['ok' => false, 'error' => 'File too large']);
return;
}
$displayFilename = $filename !== '' ? $filename : 'attachment-' . $entryNo;
header('X-Content-Type-Options: nosniff');
header('Content-Security-Policy: sandbox');
Router::download($displayFilename, $data);