phil / upload-scan-bundle
phil/upload-scan in Symfony: the scanner and its policy from configuration, a #[CleanFile] constraint for uploads, infected files quarantined outside the web root, listed on an admin page and purged after their retention, rescans on a worker, and the uploads of phil/media-bundle and the health check
Package info
github.com/parler-haut-interagir-librement/upload-scan-bundle
Type:symfony-bundle
pkg:composer/phil/upload-scan-bundle
Requires
- php: >=8.5
- ext-sodium: *
- phil/upload-scan: ^0.1@dev
- psr/clock: ^1.0
- psr/event-dispatcher: ^1.0
- psr/log: ^3.0
- symfony/config: ^8.2
- symfony/dependency-injection: ^8.2
- symfony/filesystem: ^8.2
- symfony/http-foundation: ^8.2
- symfony/validator: ^8.2
Requires (Dev)
- easycorp/easyadmin-bundle: ^5.8
- phil/media-bundle: ^0.1@dev
- phil/monitoring-bundle: ^0.1@dev
- phpunit/phpunit: ^13.4
- symfony/browser-kit: ^8.2
- symfony/console: ^8.2
- symfony/css-selector: ^8.2
- symfony/event-dispatcher: ^8.2
- symfony/event-dispatcher-contracts: ^3.6
- symfony/framework-bundle: ^8.2
- symfony/http-kernel: ^8.2
- symfony/messenger: ^8.2
- symfony/routing: ^8.2
- symfony/security-bundle: ^8.2
- symfony/security-core: ^8.2
- symfony/security-csrf: ^8.2
- symfony/translation-contracts: ^3.6
- symfony/twig-bridge: ^8.2
- symfony/twig-bundle: ^8.2
- twig/twig: ^3.21
Suggests
- easycorp/easyadmin-bundle: For QuarantineAdmin, the link of an EasyAdmin menu to the quarantine
- phil/media-bundle: To scan the files of the media library before they are stored
- phil/monitoring-bundle: For the health check of the antivirus
- symfony/console: For phil:upload-scan:purge-quarantine, the retention of the quarantine
- symfony/messenger: To scan files again on a worker
- symfony/security-csrf: With Twig, for the administration of the quarantine
- twig/twig: With the CSRF component, for the administration of the quarantine
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-15 03:41:05 UTC
README
Uploads scanned by ClamAV before they are kept, infected files quarantined where nobody can download them.
phil/upload-scan-bundle puts phil/upload-scan into a Symfony application. The scanner — clamd, the fake scanner of
the tests, or the application's own — and the policy for a file that could not be scanned come from configuration. A
#[CleanFile] constraint scans an UploadedFile as a form or a DTO is validated. An infected file is copied to a
quarantine outside the web root, under a random identifier, and announced by a FileInfected event; an operator
reads the quarantine and deletes the files dealt with on an administration page, linked from an EasyAdmin menu, which
never shows what a file holds; a file accepted
unscanned under the policy accept_and_flag is announced by FileFlagged, and a RescanFile message, which carries
identifiers only, scans it again on a worker, routed to the transport of the configuration and followed by
FileRescanned. A console command deletes the quarantined files kept longer than the retention.
When their bundles are registered, the originals of phil/media-bundle are scanned before they are stored and
phil/monitoring-bundle reports whether the antivirus answers; phil/document-import-bundle scans the documents it
reads through the UploadScanner of this bundle.
It is released under the MIT licence.
Status
| Part | State |
|---|---|
| The scanner, the policy and the quarantine from configuration | Implemented and tested |
The #[CleanFile] constraint |
Implemented and tested |
The quarantine in a directory outside public/, FileInfected and FileFlagged |
Implemented and tested |
RescanFile and its handler, over the application's RescanTarget |
Implemented and tested |
phil/media-bundle and phil/monitoring-bundle |
Implemented and tested |
The administration page of the quarantine, QuarantineInventory, the EasyAdmin menu link |
Implemented and tested |
The retention of the quarantine and phil:upload-scan:purge-quarantine |
Implemented and tested |
The route of RescanFile to a transport, FileRescanned |
Implemented and tested |
| An upload followed to the quarantine and its page in a booted application | Implemented and tested on the fake scanner |
| The clamd scanner against a real clamd | ClamdTest of phil/upload-scan and the group clamd of the bundle, run when PHIL_UPLOAD_SCAN_CLAMD names a clamd |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements, registering the bundle, a first scan. |
| Configuration | Every key of phil_upload_scan. |
| ClamAV | A clamd service beside the application in Docker Compose. |
| Validation | The #[CleanFile] constraint and the UploadScanner. |
| Quarantine and events | Where infected files go, the administration page, the retention, FileInfected and FileFlagged. |
| Rescans | RescanFile, RescanTarget, the worker and FileRescanned. |
| Integrations | The media library, the document importer, the health check. |
| Extension points | The scanner, the quarantine and the rescan targets of the application. |
| Testing | The bundle's tests, and testing an application that scans. |
| Reference | Every type and service of the bundle. |
Quick start
composer require phil/upload-scan-bundle
# config/packages/phil_upload_scan.yaml phil_upload_scan: clamd: address: tcp://clamav:3310
use Phil\UploadScanBundle\Infrastructure\Validator\CleanFile; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints as Assert; final class ManuscriptUpload { #[Assert\File(maxSize: '20M')] #[CleanFile(context: 'manuscript')] public ?UploadedFile $file = null; }
Requirements
PHP 8.5 or later, Symfony 8.2, ext-sodium, and a clamd the application can reach.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.