* Optionally scan an uploaded file with ClamAV. * * $context may include the same keys as scanSingleUploadIfEnabled(). * * Returns: * - null => scanning disabled or file clean * - ['error' => …] => infected or scan error (file is deleted) */
(string $path, array $context = [])
| 1434 | * - ['error' => …] => infected or scan error (file is deleted) |
| 1435 | */ |
| 1436 | private static function scanFileIfEnabled(string $path, array $context = []): ?array |
| 1437 | { |
| 1438 | // Respect env override + admin setting |
| 1439 | if (!self::isVirusScanEnabled()) { |
| 1440 | return null; // scanning disabled |
| 1441 | } |
| 1442 | |
| 1443 | if (!is_file($path)) { |
| 1444 | return ['error' => 'Virus scan failed: uploaded file not found.']; |
| 1445 | } |
| 1446 | |
| 1447 | if (self::isVirusScanExcluded($context)) { |
| 1448 | return null; // excluded path |
| 1449 | } |
| 1450 | |
| 1451 | if (!WorkerLauncher::canRunForeground()) { |
| 1452 | error_log('ClamAV scan skipped: PHP command execution is unavailable on this host.'); |
| 1453 | return null; |
| 1454 | } |
| 1455 | |
| 1456 | $cmd = defined('VIRUS_SCAN_CMD') ? VIRUS_SCAN_CMD : 'clamscan'; |
| 1457 | |
| 1458 | $cmdline = escapeshellcmd($cmd) |
| 1459 | . ' --stdout --no-summary ' |
| 1460 | . escapeshellarg($path) |
| 1461 | . ' 2>&1'; |
| 1462 | |
| 1463 | $output = []; |
| 1464 | $exitCode = 0; |
| 1465 | @exec($cmdline, $output, $exitCode); |
| 1466 | $msg = trim(implode("\n", $output)); |
| 1467 | |
| 1468 | // 0 = clean |
| 1469 | if ($exitCode === 0) { |
| 1470 | return null; |
| 1471 | } |
| 1472 | |
| 1473 | // 1 = virus found → block + delete + log |
| 1474 | if ($exitCode === 1) { |
| 1475 | // Allow self-test endpoints to suppress log if they pass suppressLog=true |
| 1476 | if (empty($context['suppressLog'])) { |
| 1477 | self::logVirusDetection($path, $msg, $context, $cmd, $exitCode); |
| 1478 | } |
| 1479 | @unlink($path); |
| 1480 | return [ |
| 1481 | 'error' => 'Upload blocked: virus detected in file.', |
| 1482 | ]; |
| 1483 | } |
| 1484 | |
| 1485 | // >1 = scanner error (missing DB, bad config, etc.) |
| 1486 | // Log but do NOT block the upload. |
| 1487 | error_log("ClamAV scan error (exit={$exitCode}, cmd={$cmd}): {$msg}"); |
| 1488 | return null; |
| 1489 | } |
| 1490 | |
| 1491 | /** |
| 1492 | * Recursively removes a directory and its contents. |
no test coverage detected