['pipe', 'w']], $pipes); assert(is_resource($process)); $address = trim((string) fgets($pipes[1])); app(Settings::class)->set(Setting::VirusScanningEnabled, true); app(Settings::class)->set(Setting::VirusScannerAddress, "tcp://{$address}"); app(Settings::class)->set(Setting::VirusScanMaxSizeMb, 512); return [$process, $pipes[1]]; } /** @param array{0: resource, 1: resource} $server */ function stopFakeClamd(array $server): void { fclose($server[1]); proc_terminate($server[0]); proc_close($server[0]); } test('a stream clamd hangs up on for being too long is too large, not a scanner that is down', function () { $server = startFakeClamd('limit'); // Big enough that the writes are still going when the server closes. $stream = fopen('php://temp', 'r+'); fwrite($stream, str_repeat('a', 8 * 1024 * 1024)); rewind($stream); try { $verdict = app(ClamAvScanner::class)->scan($stream, 8 * 1024 * 1024); } finally { fclose($stream); stopFakeClamd($server); } // Read as "unavailable", this file went round the retry loop and past // the unscannable policy. expect($verdict->outcome)->toBe(ScanOutcome::TooLarge); }); test('a service that is not clamd is not reported as a scanner', function () { $server = startFakeClamd('greeting'); try { $status = app(ClamAvScanner::class)->status(); } finally { stopFakeClamd($server); } // It used to be "reachable", with the first bytes of the greeting // shown as the engine's name. expect($status->reachable)->toBeFalse() ->and($status->error)->toContain('not a ClamAV scanner'); });