Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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