From 2cae337f6964dc160a0a77df5e2ad68aaa927723 Mon Sep 17 00:00:00 2001 From: Predhumeau Manon <manon.predhumeau@irstea.fr> Date: Thu, 19 Oct 2017 13:34:32 +0200 Subject: [PATCH] Add antivirus analyse (xenelop quahog) --- Listener/VirusScannerListener.php | 54 +++++++++------ Resources/config/services.yml | 28 ++++++-- Tests/Listener/VirusScannerListenerTest.php | 73 ++++++++++----------- composer.json | 3 +- 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/Listener/VirusScannerListener.php b/Listener/VirusScannerListener.php index 7c784c82..f1dd77a8 100644 --- a/Listener/VirusScannerListener.php +++ b/Listener/VirusScannerListener.php @@ -6,9 +6,10 @@ namespace Irstea\FileUploadBundle\Listener; -use CL\Tissue\Adapter\AdapterInterface; +use Xenolope\Quahog\Client; use Irstea\FileUploadBundle\Event\FileUploadCompleteEvent; use Irstea\FileUploadBundle\Exception\RejectedFileException; +use Xenolope\Quahog\Exception\ConnectionException; /** * Description of AntivirusListener. @@ -16,42 +17,57 @@ use Irstea\FileUploadBundle\Exception\RejectedFileException; class VirusScannerListener { /** - * @var AdapterInterface + * @var Client */ - private $scanner; + private $client; + /** - * @param AdapterInterface $scanner + * @param Client $client */ - public function __construct(AdapterInterface $scanner) + public function __construct(Client $client) { - $this->scanner = $scanner; + $this->client = $client; + + try { + // Check clamd server's state + $this->client->ping(); + + // Reload the virus database + $this->client->reload(); + } catch (ConnectionException $connectionException) { + $this->client->shutdown(); + $this->client = null; + } } + /** * @param FileUploadCompleteEvent $event */ public function onFileUploadCompleted(FileUploadCompleteEvent $event) { - $file = $event->getUploadedFile(); - $path = $file->getLocalPath(); - - $result = $this->scanner->scan([$path]); + if ($this->client != null) { + $file = $event->getUploadedFile(); + $path = $file->getLocalPath(); - $meta = $file->getMetadata(); + $result = $this->client->scanFile($path); + $hasVirus = $result['status'] == Client::RESULT_FOUND; - $meta['virus_scanner'] = ['has_virus' => $result->hasVirus()]; + $meta = $file->getMetadata(); + $meta['virus_scanner'] = ['has_virus' => $hasVirus]; - if ($result->hasVirus()) { - if (null !== $desc = $result->getDetections()[0]->getDescription()) { - $meta['virus_scanner']['detected'] = $desc; + if ($hasVirus) { + $desc = $result['reason']; + $meta['virus_scanner']['detected'] = $result['reason']; } - } - $file->setMetadata($meta); + $file->setMetadata($meta); - if ($result->hasVirus()) { - throw new RejectedFileException($file, $desc ? sprintf('Found the %s virus !', $desc) : 'Virus found !'); + if ($hasVirus) { + throw new RejectedFileException($file, $desc ? sprintf('Found the %s virus !', $desc) : 'Virus found !'); + } + $this->client->shutdown(); } } } diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 9b92167d..ec2f9b3a 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -10,6 +10,10 @@ parameters: irstea_file_upload.max_chunk_size: 0 + irstea_file_upload.clamav_socket_path: unix:///var/run/clamav/clamd.ctl + irstea_file_upload.client_timeout: 30 + irstea_file_upload.client_mode: PHP_NORMAL_READ + services: # Le gestionnaire de fichiers @@ -70,12 +74,24 @@ services: - "%irstea_file_upload.filesystem.name%" # Scanner anti-virus -# irstea_file_upload.virus_scanner: -# class: Irstea\FileUploadBundle\Listener\VirusScannerListener -# arguments: -# - @cl_tissue.scanner -# tags: -# - { name: kernel.event_listener, event: file_upload.complete, method: onFileUploadCompleted } + irstea_file_upload.socket: + class: Socket\Raw\Socket + arguments: + - "%irstea_file_upload.clamav_socket_path" + + irstea_file_upload.client: + class: Xenolope\Quahog\Client + arguments: + - "@irstea_file_upload.socket" + - "%irstea_file_upload.client_timeout" + - "%irstea_file_upload.client_mode" + + irstea_file_upload.virus_scanner: + class: Irstea\FileUploadBundle\Listener\VirusScannerListener + arguments: + - "@irstea_file_upload.client" + tags: + - { name: kernel.event_listener, event: file_upload.complete, method: onFileUploadCompleted } # Extension Twig irstea_file_upload.twig_extension: diff --git a/Tests/Listener/VirusScannerListenerTest.php b/Tests/Listener/VirusScannerListenerTest.php index 582da7f5..6b9ad06b 100644 --- a/Tests/Listener/VirusScannerListenerTest.php +++ b/Tests/Listener/VirusScannerListenerTest.php @@ -6,12 +6,12 @@ namespace Irstea\FileUploadBundle\Tests\Listener; -use CL\Tissue\Model\Detection; -use CL\Tissue\Model\ScanResult; use Irstea\FileUploadBundle\Event\FileUploadCompleteEvent; use Irstea\FileUploadBundle\Listener\VirusScannerListener; use PHPUnit_Framework_MockObject_MockObject; use PHPUnit_Framework_TestCase; +use Irstea\FileUploadBundle\Model\UploadedFileInterface; +use Xenolope\Quahog\Client; /** * Generated by PHPUnit_SkeletonGenerator on 2015-01-29 at 14:43:16. @@ -26,7 +26,7 @@ class VirusScannerListenerTest extends PHPUnit_Framework_TestCase /** * @var PHPUnit_Framework_MockObject_MockObject */ - protected $scanner; + protected $client; /** * @var FileUploadCompleteEvent @@ -39,42 +39,31 @@ class VirusScannerListenerTest extends PHPUnit_Framework_TestCase protected $file; /** - * {@inheritdoc} + * {@inheritDoc} */ protected function setUp() { - self::markTestSkipped('No CL\Tissue'); - } + $this->client = $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock(); - public function testOnFileUploadCompletedResultOk() - { - $result = new ScanResult( - ['foopath'], - ['foopath'], - [] - ); - $this->scanner->expects(static::once())->method('scan')->with(['foopath'])->willReturn($result); + $this->file = $this->getMock(UploadedFileInterface::class); + $this->file->expects(static::once())->method('getLocalPath')->willReturn('foopath'); + $this->file->expects(static::once())->method('getMetadata')->willReturn([]); - $this->file->expects(static::once())->method('setMetadata')->with(['virus_scanner' => ['has_virus' => false]]); + $this->event = new FileUploadCompleteEvent($this->file); - $this->listener->onFileUploadCompleted($this->event); + $this->listener = new VirusScannerListener($this->client); } - /** - * @expectedException \Irstea\FileUploadBundle\Exception\RejectedFileException - */ - public function testOnFileUploadCompletedVirusFound() + public function testOnFileUploadCompletedResultOk() { - $result = new ScanResult( - ['foopath'], - ['foopath'], - [new Detection('foopath', Detection::TYPE_VIRUS, 'BAR')] - ); - $this->scanner->expects(static::once())->method('scan')->with(['foopath'])->willReturn($result); + $result = [ + 'filename' => 'footpath', + 'reason' => null, + 'status' => Client::RESULT_OK + ]; - $this->file->expects(static::once())->method('setMetadata')->with( - ['virus_scanner' => ['has_virus' => true, 'detected' => 'BAR']] - ); + $this->client->expects(static::once())->method('scanFile')->with('foopath')->willReturn($result); + $this->file->expects(static::once())->method('setMetadata')->with(['virus_scanner' => ['has_virus' => false]]); $this->listener->onFileUploadCompleted($this->event); } @@ -82,18 +71,22 @@ class VirusScannerListenerTest extends PHPUnit_Framework_TestCase /** * @expectedException \Irstea\FileUploadBundle\Exception\RejectedFileException */ - public function testOnFileUploadCompletedVirusFoundNoDescription() + public function testOnFileUploadCompletedVirusFound() { - $result = new ScanResult( - ['foopath'], - ['foopath'], - [new Detection('foopath', Detection::TYPE_VIRUS)] - ); - $this->scanner->expects(static::once())->method('scan')->with(['foopath'])->willReturn($result); - - $this->file->expects(static::once())->method('setMetadata')->with( - ['virus_scanner' => ['has_virus' => true]] - ); + $result = [ + 'filename' => 'footpath', + 'reason' => 'Terrible virus', + 'status' => Client::RESULT_FOUND + ]; + + $this->client->expects($this->any()) + ->method('scanFile') + ->with('foopath') + ->willReturn($result); + + $this->file->expects(static::once()) + ->method('setMetadata') + ->with(['virus_scanner' => ['has_virus' => true, 'detected' => 'Terrible virus']]); $this->listener->onFileUploadCompleted($this->event); } diff --git a/composer.json b/composer.json index 6fa2391e..141f5fb8 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "ramsey/uuid": "~2.8", "blueimp/jquery-file-upload": "~9.9", "white-october/pagerfanta-bundle": "~1.0", - "willdurand/js-translation-bundle": "~2.2" + "willdurand/js-translation-bundle": "~2.2", + "xenolope/quahog": "^2.1" }, "require-dev": { "phpunit/phpunit": "^4.8" -- GitLab