SequenceAnalyzer::analyze()   F
last analyzed

Complexity

Conditions 20
Paths 513

Size

Total Lines 94
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 20
eloc 65
c 2
b 0
f 0
nc 513
nop 2
dl 0
loc 94
rs 0.6763

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (c) 2018 Matthias Held <[email protected]>
5
 * @author Matthias Held <[email protected]>
6
 * @license GNU AGPL version 3 or any later version
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License as
10
 * published by the Free Software Foundation, either version 3 of the
11
 * License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace OCA\RansomwareDetection\Analyzer;
23
24
use OCA\RansomwareDetection\Monitor;
25
use OCA\RansomwareDetection\Classifier;
26
27
class SequenceAnalyzer
28
{
29
    /**
30
     * Number of information files.
31
     *
32
     * @var int
33
     */
34
    const NUMBER_OF_INFO_FILES = 10;
35
36
    /** @var SequenceSizeAnalyzer */
37
    private $sequenceSizeAnalyzer;
38
39
    /** @var FileTypeFunnellingAnalyzer */
40
    private $fileTypeFunnellingAnalyzer;
41
42
    /** @var EntropyFunnellingAnalyzer */
43
    private $entropyFunnellingAnalyzer;
44
45
    /**
46
     * SequenceAnalyzer constructor.
47
     *
48
     * @param SequenceSizeAnalyzer       $sequenceSizeAnalyzer
49
     * @param FileTypeFunnellingAnalyzer $fileTypeFunnellingAnalyzer
50
     * @param EntropyFunnellingAnalyzer  $entropyFunnellingAnalyzer
51
     */
52
    public function __construct(
53
        SequenceSizeAnalyzer $sequenceSizeAnalyzer,
54
        FileTypeFunnellingAnalyzer $fileTypeFunnellingAnalyzer,
55
        EntropyFunnellingAnalyzer $entropyFunnellingAnalyzer
56
    ) {
57
        $this->sequenceSizeAnalyzer = $sequenceSizeAnalyzer;
58
        $this->fileTypeFunnellingAnalyzer = $fileTypeFunnellingAnalyzer;
59
        $this->entropyFunnellingAnalyzer = $entropyFunnellingAnalyzer;
60
    }
61
62
    /**
63
     * The analysis of the sequence is seperated in three parts:
64
     * The analysis: If the same number of files is deletedFilesd as written,
65
     * with the special addition that the number of written files is in the
66
     * range of [number of deletedFilesd files, number of deletedFilesd files + 4].
67
     * To enhance this analysis the sum of the size of the files deletedFilesd and
68
     * the sum of the size of the written files is compared.
69
     *
70
     * The next part is the analysis of the suspicion levels of the files written.
71
     * Therefor the suspicions levels are weighted:
72
     * Suspicious - 1
73
     * Maybe suspicious - 0.5
74
     * Not suspicious - 0
75
     *
76
     * summed up and divided by the sum of all written files. The higher the result,
77
     * the higher is the suspicion of the hole sequence.
78
     *
79
     * The last part is the file type funneling analysis.
80
     *
81
     * @param int   $sequenceId
82
     * @param array $sequence
83
     *
84
     * @return SequenceResult
85
     */
86
    public function analyze($sequenceId, $sequence)
87
    {
88
        $sequenceResult = new SequenceResult($sequenceId, 0, 0, 0, 0, $sequence);
89
        if (sizeof($sequence) === 0) {
90
            return $sequenceResult;
91
        }
92
93
        $files = ['written' => [], 'sizeWritten' => 0, 'deleted' => [], 'sizeDeleted' => 0, 'suspicious' => [], 'maybeSuspicious' => [], 'notSuspicious' => []];
94
        $suspicionScore = 0;
95
96
        foreach ($sequence as $file) {
97
            if ($file->getType() === 'file') {
98
                switch ($file->getCommand()) {
99
                    case Monitor::WRITE:
100
                        $files['written'][] = $file;
101
                        $files['sizeWritten'] = $files['sizeWritten'] + $file->getSize();
102
                        break;
103
                    case Monitor::READ:
104
                        break;
105
                    case Monitor::RENAME:
106
                        break;
107
                    case Monitor::DELETE:
108
                        $files['deleted'][] = $file;
109
                        $files['sizeDeleted'] = $files['sizeDeleted'] + $file->getSize();
110
                        break;
111
                    case Monitor::CREATE:
112
                        break;
113
                    default:
114
                        break;
115
                }
116
                switch ($file->getSuspicionClass()) {
117
                    case Classifier::SUSPICIOUS:
118
                        $files['suspicious'][] = $file;
119
                        break;
120
                    case Classifier::MAYBE_SUSPICIOUS:
121
                        $files['maybeSuspicious'][] = $file;
122
                        break;
123
                    case Classifier::NOT_SUSPICIOUS:
124
                        $files['notSuspicious'][] = $file;
125
                        break;
126
                    case Classifier::NO_INFORMATION:
127
                        break;
128
                    default:
129
                        break;
130
                }
131
            }
132
        }
133
134
        // compare files written and files deleted
135
        if (sizeof($files['written']) > 0 && sizeof($files['deleted']) > 0) {
136
            $sequenceResult->setSizeWritten($files['sizeWritten']);
137
            $sequenceResult->setSizeDeleted($files['sizeDeleted']);
138
            $upperBound = sizeof($files['deleted']) + self::NUMBER_OF_INFO_FILES;
139
            if (sizeof($files['written']) <= $upperBound && sizeof($files['written']) >= sizeof($files['deleted'])) {
140
                if ($this->sequenceSizeAnalyzer->analyze($sequence) === SequenceSizeAnalyzer::EQUAL_SIZE) {
141
                    $sequenceResult->setQuantities(2);
142
                    $suspicionScore += 1;
143
                } else {
144
                    $sequenceResult->setQuantities(1);
145
                    $suspicionScore += 0;
146
                }
147
            }
148
        }
149
150
        $numberOfWrittenFiles = sizeof($files['suspicious']) + sizeof($files['maybeSuspicious']) + sizeof($files['notSuspicious']);
151
152
        // remove info files from the weight
153
        $numberOfInfoFiles = self::NUMBER_OF_INFO_FILES;
154
        if (sizeof($files['notSuspicious']) < self::NUMBER_OF_INFO_FILES) {
155
            $numberOfInfoFiles = sizeof($files['notSuspicious']);
156
        }
157
158
        // weight the suspicion levels.
159
        $suspicionSum = (sizeof($files['suspicious']) * 1) + (sizeof($files['maybeSuspicious']) * 0.5);
160
161
        // check for division by zero.
162
        if (($numberOfWrittenFiles - $numberOfInfoFiles) > 0) {
163
            $sequenceResult->setFileSuspicion($suspicionSum / ($numberOfWrittenFiles - $numberOfInfoFiles));
164
            $suspicionScore += $suspicionSum / ($numberOfWrittenFiles - $numberOfInfoFiles);
165
        }
166
167
        // entropy funnelling
168
        $entropyFunnelling = $this->entropyFunnellingAnalyzer->analyze($files['deleted'], $files['written']);
169
        $sequenceResult->setEntropyFunnelling($entropyFunnelling);
170
        $suspicionScore += $entropyFunnelling->getEntropyFunnelling();
171
172
        // check for file type funneling
173
        $fileTypeFunnelling = $this->fileTypeFunnellingAnalyzer->analyze($sequence);
174
        $sequenceResult->setFileTypeFunnelling($fileTypeFunnelling);
175
        $suspicionScore += $fileTypeFunnelling;
176
177
        $sequenceResult->setSuspicionScore($suspicionScore);
178
179
        return $sequenceResult;
180
    }
181
}
182