A clean, Laravel-native way to scan uploaded files using ClamAV. This package provides a simple API for virus scanning in your file upload validation and storage pipelines.
You can install the package via composer:
composer require jodeveloper/upload-file-scannerYou can publish the config file with:
php artisan vendor:publish --tag="clamav-scanner-config"This is the contents of the published config file:
return [
'binary' => env('CLAMAV_BINARY', 'clamscan'),
'timeout' => (int) env('CLAMAV_TIMEOUT', 30),
'scan_options' => [
'--no-summary',
],
];- PHP 8.3 or higher
- Laravel 11.0 or 12.0
- ClamAV installed on your server (clamscan binary)
brew install clamavAfter installation, you may need to update the virus definitions:
freshclamsudo apt-get update
sudo apt-get install clamav clamav-daemonUpdate virus definitions:
sudo freshclamsudo yum install epel-release
sudo yum install clamav clamav-updateUpdate virus definitions:
sudo freshclamFor detailed instructions on installing ClamAV from source, see the official ClamAV documentation.
After installation, verify that ClamAV is accessible:
clamscan --versionThis should display ClamAV version information.
use Jodeveloper\UploadFileScanner\Facades\Scanner;
$result = Scanner::scan('/path/to/file');
if ($result->hasVirus()) {
// Handle infected file
}
if ($result->isClean()) {
// File is safe to process
}
// Get the scanner output
$output = $result->output;The package provides a Laravel validation rule for easy integration:
Simple approach (recommended):
public function upload(Request $request)
{
$validated = $request->validate([
'file' => ['required', 'file', 'clean_file'],
]);
// File is clean, proceed with storage
}For more control, use the object-based approach:
use Jodeveloper\UploadFileScanner\Rules\CleanFile;
use Jodeveloper\UploadFileScanner\Contracts\Scanner;
public function upload(Request $request)
{
$validated = $request->validate([
'file' => ['required', 'file', new CleanFile()],
]);
// File is clean, proceed with storage
}use Illuminate\Http\Request;
use Jodeveloper\UploadFileScanner\Contracts\Scanner;
use Jodeveloper\UploadFileScanner\Exceptions\ScanFailedException;
class FileUploadController extends Controller
{
public function store(Request $request, Scanner $scanner)
{
$request->validate([
'file' => ['required', 'file', 'max:10240'], // max 10MB
]);
try {
$result = $scanner->scan($request->file('file')->getRealPath());
if ($result->hasVirus()) {
return back()->with('error', 'The uploaded file contains a virus.');
}
// File is clean, store it
$path = $request->file('file')->store('uploads');
return back()->with('success', 'File uploaded successfully.');
} catch (ScanFailedException $e) {
// Handle scanner execution failure
return back()->with('error', 'Unable to scan file. Please try again.');
}
}
}By default, the package assumes clamscan is in your system PATH. If you have a custom installation:
CLAMAV_BINARY=/usr/local/bin/clamscanThe default timeout is 30 seconds. Adjust for large files:
CLAMAV_TIMEOUT=60Add additional options to pass to clamscan in the config file:
'scan_options' => [
'--no-summary',
'--infected',
],Warning: Use caution with options like --remove which will delete infected files.
This package provides virus scanning as a secondary security layer. It should not be your only defense against malicious file uploads.
- Re-encoding Images: Always re-encode uploaded images to strip potential embedded payloads
- File Type Validation: Validate MIME types and file extensions
- Content Inspection: Inspect file contents, not just extensions
- Storage Location: Store uploads outside of the public web root
- Access Control: Implement proper authentication and authorization
SVG files can contain JavaScript and should be treated with extreme caution. Always sanitize SVG files before storage or serving.
Never store user uploads in publicly accessible directories without proper access controls. Use Laravel's Storage::disk('local') or implement signed URLs for public access.
The package throws ScanFailedException when:
- ClamAV binary is not found
- Process crashes or fails to execute
- Timeout occurs
- Other execution errors occur
Infected files do not throw exceptions. They return a ScanResult where hasVirus() returns true.
use Jodeveloper\UploadFileScanner\Exceptions\ScanFailedException;
try {
$result = $scanner->scan($path);
} catch (ScanFailedException $e) {
// Log the error and notify administrators
Log::error('ClamAV scan failed', ['message' => $e->getMessage()]);
throw new \RuntimeException('Unable to scan file. Please try again later.');
}use Jodeveloper\UploadFileScanner\Facades\Scanner;
Scanner::scan(string $path): ScanResultuse Jodeveloper\UploadFileScanner\Contracts\Scanner;
public function __construct(Scanner $scanner)The ScanResult object is immutable and exposes readonly properties:
public readonly bool $clean
public readonly string $outputHelper methods are also available:
isClean(): bool // Returns true if no virus was found
hasVirus(): bool // Returns true if a virus was detectedImplements Illuminate\Contracts\Validation\Rule. Can be used as an object or string rule (clean_file).
composer testThe test suite mocks all ClamAV execution - no actual scanning occurs during tests.
- This package does not provide automatic scanning of all uploads
- No UI is included
- No opinionated storage logic is provided
- ClamAV must be installed and accessible on your server
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.
