jodeveloper / upload-file-scanner
A Laravel package for scanning uploaded files using ClamAV
Fund package maintenance!
:vendor_name
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/jodeveloper/upload-file-scanner
Requires
- php: ^8.3
- illuminate/contracts: ^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
- symfony/process: ^7.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- spatie/laravel-ray: ^1.35
README
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.
Installation
You can install the package via composer:
composer require jodeveloper/upload-file-scanner
You 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', ], ];
Requirements
- PHP 8.3 or higher
- Laravel 11.0 or 12.0
- ClamAV installed on your server (clamscan binary)
Installing ClamAV
macOS
brew install clamav
After installation, you may need to update the virus definitions:
freshclam
Ubuntu/Debian
sudo apt-get update sudo apt-get install clamav clamav-daemon
Update virus definitions:
sudo freshclam
CentOS/RHEL
sudo yum install epel-release sudo yum install clamav clamav-update
Update virus definitions:
sudo freshclam
From Source
For detailed instructions on installing ClamAV from source, see the official ClamAV documentation.
Verifying Installation
After installation, verify that ClamAV is accessible:
clamscan --version
This should display ClamAV version information.
Usage
Using the Facade (Recommended)
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;
Using the Validation Rule
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 }
Example Controller
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.'); } } }
Configuration
ClamAV Binary Path
By default, the package assumes clamscan is in your system PATH. If you have a custom installation:
CLAMAV_BINARY=/usr/local/bin/clamscan
Scan Timeout
The default timeout is 30 seconds. Adjust for large files:
CLAMAV_TIMEOUT=60
Scan Options
Add 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.
Security Philosophy
This Package is a Secondary Defense
This package provides virus scanning as a secondary security layer. It should not be your only defense against malicious file uploads.
Required Additional Security Measures
- 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 are Unsafe
SVG files can contain JavaScript and should be treated with extreme caution. Always sanitize SVG files before storage or serving.
Public Storage is Dangerous
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.
Exception Handling
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.'); }
API Reference
Facade
use Jodeveloper\UploadFileScanner\Facades\Scanner; Scanner::scan(string $path): ScanResult
Contract
use Jodeveloper\UploadFileScanner\Contracts\Scanner; public function __construct(Scanner $scanner)
ScanResult
The ScanResult object is immutable and exposes readonly properties:
public readonly bool $clean public readonly string $output
Helper methods are also available:
isClean(): bool // Returns true if no virus was found hasVirus(): bool // Returns true if a virus was detected
CleanFile Rule
Implements Illuminate\Contracts\Validation\Rule. Can be used as an object or string rule (clean_file).
Testing
composer test
The test suite mocks all ClamAV execution - no actual scanning occurs during tests.
Limitations
- 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
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.