Installation:
composer require sunspikes/clamav-validator
Publish the config file:
php artisan vendor:publish --provider="Sunspikes\ClamavValidator\ClamavValidatorServiceProvider" --tag="config"
First Use Case: Validate a file upload in a Laravel controller or form request:
use Sunspikes\ClamavValidator\Rules\ClamavScan;
$request->validate([
'file' => ['required', 'file', new ClamavScan],
]);
Where to Look First:
config/clamav-validator.php for default settings (e.g., timeout, max_file_size).app/Rules/ClamavScan.php (if extended) or the package’s Rules\ClamavScan for customization hooks.Basic File Validation:
$request->validate([
'avatar' => ['required', 'image', 'mimes:jpeg,png', new ClamavScan],
]);
Customizing Scan Behavior:
'timeout' => 10).'max_file_size' => 10240 for 10MB).$request->validate([
'file' => [new ClamavScan(['exclude_extensions' => ['pdf', 'docx']])],
]);
Integration with Form Requests:
use Sunspikes\ClamavValidator\Rules\ClamavScan;
public function rules()
{
return [
'resume' => ['required', 'file', new ClamavScan(['on_failure' => 'skip'])],
];
}
public function messages()
{
return [
'resume.clamav' => 'The file contains a virus and cannot be processed.',
];
}
Manual Scanning (Advanced):
Use the underlying ClamavScanner service directly:
use Sunspikes\ClamavValidator\Facades\ClamavScanner;
$path = $request->file('file')->store('temp');
$result = ClamavScanner::scan($path);
if ($result->hasVirus()) {
throw new \Exception('Virus detected!');
}
Batch Processing: Scan multiple files in a loop (e.g., for bulk uploads):
foreach ($request->file('files') as $file) {
$path = $file->store('temp');
if (!ClamavScanner::scan($path)->isClean()) {
return back()->withErrors(['files' => 'Virus detected in one or more files.']);
}
}
ClamAV Service Dependencies:
Connection refused or Command not found if ClamAV isn’t installed/running.sudo systemctl status clamav-daemon) and freshclam is updated.config/clamav-validator.php for the correct clamav_command (default: clamscan).Performance Overhead:
timeout in config (default: 5 seconds).on_failure: 'skip' to bypass scans for non-critical files.False Positives/Negatives:
sudo freshclam regularly to avoid outdated signatures./etc/clamav/local.d/ if needed.File Size Limits:
File too large if exceeding max_file_size (default: 10485760 bytes = 10MB).$request->validate([
'file' => ['file', 'max:20480', new ClamavScan(['max_file_size' => 20971520])],
]);
Windows Compatibility:
Enable Logging:
Add to config/clamav-validator.php:
'log_errors' => true,
Check storage/logs/laravel.log for ClamAV scan failures.
Test Locally: Use a test file (e.g., EICAR test file) to verify scans:
echo "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" > eicar.com
Then upload it to trigger the validator.
Custom Error Messages: Override default messages in your request:
public function messages()
{
return [
'file.clamav' => 'This file is infected with malware. Please clean it and try again.',
];
}
Custom Rules:
Extend the ClamavScan rule for project-specific logic:
namespace App\Rules;
use Sunspikes\ClamavValidator\Rules\ClamavScan as BaseClamavScan;
class ProjectClamavScan extends BaseClamavScan
{
public function __construct(array $options = [])
{
$options['exclude_extensions'] = array_merge(
$options['exclude_extensions'] ?? [],
['iso', 'bin']
);
parent::__construct($options);
}
}
Hook into Scan Events: Publish the service provider and bind events:
// In AppServiceProvider@boot()
\Sunspikes\ClamavValidator\Events\ClamavScanStarted::class => [YourListener::class, 'handle'],
\Sunspikes\ClamavValidator\Events\ClamavScanCompleted::class => [YourListener::class, 'handle'],
Mocking for Tests: Use a fake scanner in tests:
$this->app->bind(\Sunspikes\ClamavValidator\Contracts\ClamavScanner::class, function () {
return new \Sunspikes\ClamavValidator\Testing\FakeClamavScanner(['has_virus' => false]);
});
Docker Integration:
Add ClamAV to your Dockerfile:
RUN apt-get update && apt-get install -y clamav-daemon && freshclam
CMD (service clamav-daemon start && tail -f /dev/null)
How can I help you explore Laravel packages today?