Completed
Pull Request — master (#32)
by Krishnaprasad
04:46
created

ClamavValidator::getClamavSocket()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 6
cts 8
cp 0.75
rs 9.8333
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 3.1406
1
<?php namespace Sunspikes\ClamavValidator;
2
3
use Illuminate\Contracts\Translation\Translator;
4
use Illuminate\Support\Arr;
5
use Illuminate\Support\Facades\Config;
6
use Illuminate\Validation\Validator;
7
use Xenolope\Quahog\Client as QuahogClient;
8
use Socket\Raw\Factory as SocketFactory;
9
use Symfony\Component\HttpFoundation\File\UploadedFile;
10
11
class ClamavValidator extends Validator
12
{
13
    /**
14
     * Creates a new instance of ClamavValidator.
15
     *
16
     * ClamavValidator constructor.
17
     * @param Translator $translator
18
     * @param array      $data
19
     * @param array      $rules
20
     * @param array      $messages
21
     * @param array      $customAttributes
22
     */
23 4
    public function __construct(
24
        Translator $translator,
25
        array $data,
26
        array $rules,
27
        array $messages = [],
28
        array $customAttributes = []
29
    ) {
30 4
        parent::__construct($translator, $data, $rules, $messages, $customAttributes);
31 4
    }
32
33
    /**
34
     * Validate the uploaded file for virus/malware with ClamAV.
35
     *
36
     * @param  $attribute   string
37
     * @param  $value       mixed
38
     * @param  $parameters  array
39
     *
40
     * @return boolean
41
     * @throws ClamavValidatorException
42
     */
43 3
    public function validateClamav($attribute, $value, $parameters)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parameters is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
44
    {
45 3
        if (true === Config::get('clamav.skip_validation')) {
46
            return true;
47
        }
48
49 3
        $file = $this->getFilePath($value);
50 3
        if (! is_readable($file)) {
51 1
            throw ClamavValidatorException::forNonReadableFile($file);
52
        }
53
54
        try {
55 2
            $socket  = $this->getClamavSocket();
56 2
            $scanner = $this->createQuahogScannerClient($socket);
57 2
            $result  = $scanner->scanResourceStream(fopen($file, 'rb'));
58 2
        } catch (\Exception $exception) {
59
            throw ClamavValidatorException::forClientException($exception);
60
        }
61
62 2
        if (QuahogClient::RESULT_ERROR === $result['status']) {
63
            throw ClamavValidatorException::forScanResult($result);
64
        }
65
66
        // Check if scan result is clean
67 2
        return QuahogClient::RESULT_OK === $result['status'];
68
    }
69
70
    /**
71
     * Guess the ClamAV socket.
72
     *
73
     * @return string
74
     */
75 2
    protected function getClamavSocket()
76
    {
77 2
        $preferredSocket = Config::get('clamav.preferred_socket');
78
79 2
        if ($preferredSocket === 'unix_socket') {
80 2
            $unixSocket = Config::get('clamav.unix_socket');
81 2
            if (file_exists($unixSocket)) {
82 2
                return 'unix://' . $unixSocket;
83
            }
84
        }
85
        // We use the tcp_socket as fallback as well
86
        return Config::get('clamav.tcp_socket');
87
    }
88
89
    /**
90
     * Return the file path from the passed object.
91
     *
92
     * @param mixed $file
93
     * @return string
94
     */
95 3
    protected function getFilePath($file)
96
    {
97
        // if were passed an instance of UploadedFile, return the path
98 3
        if ($file instanceof UploadedFile) {
99
            return $file->getPathname();
100
        }
101
102
        // if we're passed a PHP file upload array, return the "tmp_name"
103 3
        if (is_array($file) && null !== Arr::get($file, 'tmp_name')) {
104
            return $file['tmp_name'];
105
        }
106
107
        // fallback: we were likely passed a path already
108 3
        return $file;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $file; (object|integer|double|string|null|boolean|array) is incompatible with the return type documented by Sunspikes\ClamavValidato...vValidator::getFilePath of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
109
    }
110
111
    /**
112
     * Create a new quahog ClamAV scanner client.
113
     *
114
     * @param string $socket
115
     * @return QuahogClient
116
     */
117 2
    protected function createQuahogScannerClient($socket)
118
    {
119
        // Create a new client socket instance
120 2
        $client = (new SocketFactory())->createClient($socket);
121
122 2
        return new QuahogClient($client, Config::get('clamav.socket_read_timeout'), PHP_NORMAL_READ);
123
    }
124
}
125