Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace antivirus_clamav;
  18  
  19  /**
  20   * Tests for ClamAV antivirus scanner class.
  21   *
  22   * @package    antivirus_clamav
  23   * @category   test
  24   * @copyright  2016 Ruslan Kabalin, Lancaster University.
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class scanner_test extends \advanced_testcase {
  28      /** @var string temporary file used in testing */
  29      protected $tempfile;
  30  
  31      protected function setUp(): void {
  32          $this->resetAfterTest();
  33  
  34          // Create tempfile.
  35          $tempfolder = make_request_directory(false);
  36          $this->tempfile = $tempfolder . '/' . rand();
  37          touch($this->tempfile);
  38      }
  39  
  40      protected function tearDown(): void {
  41          @unlink($this->tempfile);
  42      }
  43  
  44      public function test_scan_file_not_exists() {
  45          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
  46              ->onlyMethods(array('scan_file_execute_commandline', 'message_admins'))
  47              ->getMock();
  48  
  49          // Test specifying file that does not exist.
  50          $nonexistingfile = $this->tempfile . '_';
  51          $this->assertFileDoesNotExist($nonexistingfile);
  52          // Run mock scanning, we expect SCAN_RESULT_ERROR.
  53          $this->assertEquals(2, $antivirus->scan_file($nonexistingfile, ''));
  54          $this->assertDebuggingCalled();
  55      }
  56  
  57      public function test_scan_file_no_virus() {
  58          $methods = array(
  59              'scan_file_execute_commandline',
  60              'scan_file_execute_socket',
  61              'message_admins',
  62              'get_config',
  63          );
  64          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
  65              ->onlyMethods($methods)
  66              ->getMock();
  67          // Initiate mock scanning with configuration setting to use commandline.
  68          $configmap = array(array('runningmethod', 'commandline'));
  69          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
  70  
  71          // Configure scan_file_execute_commandline and scan_file_execute_socket
  72          // method stubs to behave as if no virus has been found (SCAN_RESULT_OK).
  73          $antivirus->method('scan_file_execute_commandline')->willReturn(0);
  74          $antivirus->method('scan_file_execute_socket')->willReturn(0);
  75  
  76          // Set expectation that message_admins is NOT called.
  77          $antivirus->expects($this->never())->method('message_admins');
  78  
  79          // Run mock scanning.
  80          $this->assertFileExists($this->tempfile);
  81          $this->assertEquals(0, $antivirus->scan_file($this->tempfile, ''));
  82  
  83          // Initiate mock scanning with configuration setting to use unixsocket.
  84          $configmap = array(array('runningmethod', 'unixsocket'));
  85          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
  86  
  87          // Run mock scanning.
  88          $this->assertEquals(0, $antivirus->scan_file($this->tempfile, ''));
  89  
  90          // Initiate mock scanning with configuration setting to use tcpsocket.
  91          $configmap = array(array('runningmethod', 'tcpsocket'));
  92          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
  93  
  94          // Run mock scanning.
  95          $this->assertEquals(0, $antivirus->scan_file($this->tempfile, ''));
  96      }
  97  
  98      public function test_scan_file_virus() {
  99          $methods = array(
 100              'scan_file_execute_commandline',
 101              'scan_file_execute_socket',
 102              'message_admins',
 103              'get_config',
 104          );
 105          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 106              ->onlyMethods($methods)
 107              ->getMock();
 108          // Initiate mock scanning with configuration setting to use commandline.
 109          $configmap = array(array('runningmethod', 'commandline'));
 110          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 111  
 112          // Configure scan_file_execute_commandline and scan_file_execute_socket
 113          // method stubs to behave as if virus has been found (SCAN_RESULT_FOUND).
 114          $antivirus->method('scan_file_execute_commandline')->willReturn(1);
 115          $antivirus->method('scan_file_execute_socket')->willReturn(1);
 116  
 117          // Set expectation that message_admins is NOT called.
 118          $antivirus->expects($this->never())->method('message_admins');
 119  
 120          // Run mock scanning.
 121          $this->assertFileExists($this->tempfile);
 122          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 123  
 124          // Initiate mock scanning with configuration setting to use unixsocket.
 125          $configmap = array(array('runningmethod', 'unixsocket'));
 126          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 127  
 128          // Run mock scanning.
 129          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 130  
 131          // Initiate mock scanning with configuration setting to use tcpsocket.
 132          $configmap = array(array('runningmethod', 'tcpsocket'));
 133          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 134  
 135          // Run mock scanning.
 136          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 137      }
 138  
 139      public function test_scan_file_error_donothing() {
 140          $methods = array(
 141              'scan_file_execute_commandline',
 142              'scan_file_execute_socket',
 143              'message_admins',
 144              'get_config',
 145              'get_scanning_notice',
 146          );
 147          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 148              ->onlyMethods($methods)
 149              ->getMock();
 150  
 151          // Configure scan_file_execute_commandline and scan_file_execute_socket
 152          // method stubs to behave as if there is a scanning error (SCAN_RESULT_ERROR).
 153          $antivirus->method('scan_file_execute_commandline')->willReturn(2);
 154          $antivirus->method('scan_file_execute_socket')->willReturn(2);
 155          $antivirus->method('get_scanning_notice')->willReturn('someerror');
 156  
 157          // Set expectation that message_admins is called.
 158          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 159  
 160          // Initiate mock scanning with configuration setting to do nothing on
 161          // scanning error and using commandline.
 162          $configmap = array(array('clamfailureonupload', 'donothing'), array('runningmethod', 'commandline'));
 163          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 164  
 165          // Run mock scanning.
 166          $this->assertFileExists($this->tempfile);
 167          $this->assertEquals(2, $antivirus->scan_file($this->tempfile, ''));
 168  
 169          // Initiate mock scanning with configuration setting to do nothing on
 170          // scanning error and using unixsocket.
 171          $configmap = array(array('clamfailureonupload', 'donothing'), array('runningmethod', 'unixsocket'));
 172          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 173  
 174          // Run mock scanning.
 175          $this->assertEquals(2, $antivirus->scan_file($this->tempfile, ''));
 176  
 177          // Initiate mock scanning with configuration setting to do nothing on
 178          // scanning error and using tcpsocket.
 179          $configmap = array(array('clamfailureonupload', 'donothing'), array('runningmethod', 'tcpsocket'));
 180          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 181  
 182          // Run mock scanning.
 183          $this->assertEquals(2, $antivirus->scan_file($this->tempfile, ''));
 184      }
 185  
 186      public function test_scan_file_error_actlikevirus() {
 187          $methods = array(
 188              'scan_file_execute_commandline',
 189              'scan_file_execute_socket',
 190              'message_admins',
 191              'get_config',
 192              'get_scanning_notice',
 193          );
 194          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 195              ->onlyMethods($methods)
 196              ->getMock();
 197  
 198          // Configure scan_file_execute_commandline and scan_file_execute_socket
 199          // method stubs to behave as if there is a scanning error (SCAN_RESULT_ERROR).
 200          $antivirus->method('scan_file_execute_commandline')->willReturn(2);
 201          $antivirus->method('scan_file_execute_socket')->willReturn(2);
 202          $antivirus->method('get_scanning_notice')->willReturn('someerror');
 203  
 204          // Set expectation that message_admins is called.
 205          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 206  
 207          // Initiate mock scanning with configuration setting to act like virus on
 208          // scanning error and using commandline.
 209          $configmap = array(array('clamfailureonupload', 'actlikevirus'), array('runningmethod', 'commandline'));
 210          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 211  
 212          // Run mock scanning, we expect SCAN_RESULT_FOUND since configuration
 213          // require us to act like virus.
 214          $this->assertFileExists($this->tempfile);
 215          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 216  
 217          // Initiate mock scanning with configuration setting to act like virus on
 218          // scanning error and using unixsocket.
 219          $configmap = array(array('clamfailureonupload', 'actlikevirus'), array('runningmethod', 'unixsocket'));
 220          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 221  
 222          // Run mock scanning, we expect SCAN_RESULT_FOUND since configuration
 223          // require us to act like virus.
 224          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 225  
 226          // Initiate mock scanning with configuration setting to act like virus on
 227          // scanning error and using tcpsocket.
 228          $configmap = array(array('clamfailureonupload', 'actlikevirus'), array('runningmethod', 'tcpsocket'));
 229          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 230  
 231          // Run mock scanning, we expect SCAN_RESULT_FOUND since configuration
 232          // require us to act like virus.
 233          $this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
 234      }
 235  
 236      public function test_scan_file_error_tryagain() {
 237          $methods = array(
 238                  'scan_file_execute_commandline',
 239                  'scan_file_execute_unixsocket',
 240                  'message_admins',
 241                  'get_config',
 242                  'get_scanning_notice',
 243          );
 244          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')->onlyMethods($methods)->getMock();
 245  
 246          // Configure scan_file_execute_commandline and scan_file_execute_unixsocket
 247          // method stubs to behave as if there is a scanning error (SCAN_RESULT_ERROR).
 248          $antivirus->method('scan_file_execute_commandline')->willReturn(2);
 249          $antivirus->method('scan_file_execute_unixsocket')->willReturn(2);
 250          $antivirus->method('get_scanning_notice')->willReturn('someerror');
 251  
 252          // Set expectation that message_admins is called.
 253          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 254  
 255          // Initiate mock scanning with configuration setting to act like virus on
 256          // scanning error and using commandline.
 257          $configmap = array(array('clamfailureonupload', 'tryagain'), array('runningmethod', 'commandline'));
 258          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 259  
 260          // Run mock scanning.
 261          $this->assertFileExists($this->tempfile);
 262          $this->expectException(\core\antivirus\scanner_exception::class);
 263          $antivirus->scan_file($this->tempfile, '');
 264          $this->assertEquals('antivirusfailed', $this->getExpectedExceptionCode());
 265          $this->assertFileDoesNotExist($this->tempfile);
 266      }
 267  
 268      public function test_scan_data_no_virus() {
 269          $methods = array(
 270              'scan_data_execute_socket',
 271              'message_admins',
 272              'get_config',
 273          );
 274          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 275              ->onlyMethods($methods)
 276              ->getMock();
 277          // Initiate mock scanning with configuration setting to use unixsocket.
 278          $configmap = array(array('runningmethod', 'unixsocket'));
 279          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 280  
 281          // Configure scan_data_execute_socket method stubs to behave as if
 282          // no virus has been found (SCAN_RESULT_OK).
 283          $antivirus->method('scan_data_execute_socket')->willReturn(0);
 284  
 285          // Set expectation that message_admins is NOT called.
 286          $antivirus->expects($this->never())->method('message_admins');
 287  
 288          // Run mock scanning.
 289          $this->assertEquals(0, $antivirus->scan_data(''));
 290  
 291          // Re-initiate mock scanning with configuration setting to use tcpsocket.
 292          $configmap = array(array('runningmethod', 'tcpsocket'));
 293          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 294  
 295          // Set expectation that message_admins is NOT called.
 296          $antivirus->expects($this->never())->method('message_admins');
 297  
 298          // Run mock scanning.
 299          $this->assertEquals(0, $antivirus->scan_data(''));
 300      }
 301  
 302      public function test_scan_data_virus() {
 303          $methods = array(
 304              'scan_data_execute_socket',
 305              'message_admins',
 306              'get_config',
 307          );
 308          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 309              ->onlyMethods($methods)
 310              ->getMock();
 311          // Initiate mock scanning with configuration setting to use unixsocket.
 312          $configmap = array(array('runningmethod', 'unixsocket'));
 313          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 314  
 315          // Configure scan_data_execute_socket method stubs to behave as if
 316          // no virus has been found (SCAN_RESULT_FOUND).
 317          $antivirus->method('scan_data_execute_socket')->willReturn(1);
 318  
 319          // Set expectation that message_admins is NOT called.
 320          $antivirus->expects($this->never())->method('message_admins');
 321  
 322          // Run mock scanning.
 323          $this->assertEquals(1, $antivirus->scan_data(''));
 324  
 325          // Re-initiate mock scanning with configuration setting to use tcpsocket.
 326          $configmap = array(array('runningmethod', 'tcpsocket'));
 327          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 328  
 329          // Set expectation that message_admins is NOT called.
 330          $antivirus->expects($this->never())->method('message_admins');
 331  
 332          // Run mock scanning.
 333          $this->assertEquals(1, $antivirus->scan_data(''));
 334      }
 335  
 336      public function test_scan_data_error_donothing() {
 337          $methods = array(
 338              'scan_data_execute_socket',
 339              'message_admins',
 340              'get_config',
 341              'get_scanning_notice',
 342          );
 343          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 344              ->onlyMethods($methods)
 345              ->getMock();
 346          // Initiate mock scanning with configuration setting to do nothing on
 347          // scanning error and using unixsocket.
 348          $configmap = array(array('clamfailureonupload', 'donothing'), array('runningmethod', 'unixsocket'));
 349          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 350  
 351          // Configure scan_data_execute_socket method stubs to behave as if
 352          // there is a scanning error (SCAN_RESULT_ERROR).
 353          $antivirus->method('scan_data_execute_socket')->willReturn(2);
 354          $antivirus->method('get_scanning_notice')->willReturn('someerror');
 355  
 356          // Set expectation that message_admins is called.
 357          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 358  
 359          // Run mock scanning.
 360          $this->assertEquals(2, $antivirus->scan_data(''));
 361  
 362          // Re-initiate mock scanning with configuration setting to do nothing on
 363          // scanning error and using tcsocket.
 364          $configmap = array(array('clamfailureonupload', 'donothing'), array('runningmethod', 'tcpsocket'));
 365          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 366  
 367          // Set expectation that message_admins is called.
 368          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 369  
 370          // Run mock scanning.
 371          $this->assertEquals(2, $antivirus->scan_data(''));
 372      }
 373  
 374      public function test_scan_data_error_actlikevirus() {
 375          $methods = array(
 376              'scan_data_execute_socket',
 377              'message_admins',
 378              'get_config',
 379              'get_scanning_notice',
 380          );
 381          $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')
 382              ->onlyMethods($methods)
 383              ->getMock();
 384  
 385          // Initiate mock scanning with configuration setting to act like virus on
 386          // scanning error and using unixsocket.
 387          $configmap = array(array('clamfailureonupload', 'actlikevirus'), array('runningmethod', 'unixsocket'));
 388          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 389  
 390          // Configure scan_data_execute_socket method stubs to behave as if
 391          // there is a scanning error (SCAN_RESULT_ERROR).
 392          $antivirus->method('scan_data_execute_socket')->willReturn(2);
 393          $antivirus->method('get_scanning_notice')->willReturn('someerror');
 394  
 395          // Set expectation that message_admins is called.
 396          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 397  
 398          // Run mock scanning, we expect SCAN_RESULT_FOUND since configuration
 399          // require us to act like virus.
 400          $this->assertEquals(1, $antivirus->scan_data(''));
 401  
 402          // Re-initiate mock scanning with configuration setting to act like virus on
 403          // scanning error and using tcpsocket.
 404          $configmap = array(array('clamfailureonupload', 'actlikevirus'), array('runningmethod', 'tcpsocket'));
 405          $antivirus->method('get_config')->will($this->returnValueMap($configmap));
 406  
 407          // Set expectation that message_admins is called.
 408          $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
 409  
 410          // Run mock scanning, we expect SCAN_RESULT_FOUND since configuration
 411          // require us to act like virus.
 412          $this->assertEquals(1, $antivirus->scan_data(''));
 413      }
 414  }