Passed
Push — develop ( 3bea6f...0f8f07 )
by Mark
36:13
created

XmlScanner   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Test Coverage

Coverage 79.31%

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 79
ccs 23
cts 29
cp 0.7931
rs 10
c 0
b 0
f 0
wmc 12

4 Methods

Rating   Name   Duplication   Size   Complexity  
A identifyLibxmlDisableEntityLoaderAvailability() 0 16 5
A scanFile() 0 3 1
A __construct() 0 7 2
A scan() 0 17 4
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Security;
4
5
use PhpOffice\PhpSpreadsheet\Reader\Exception;
6
7
class XmlScanner
8
{
9
    /**
10
     * Identifies whether the thread-safe libxmlDisableEntityLoader() function is available.
11
     *
12
     * @var bool
13
     */
14
    private $libxmlDisableEntityLoader = false;
15
16
    private $pattern;
17
18 96
    public function __construct($pattern = '<!DOCTYPE')
19
    {
20 96
        $this->pattern = $pattern;
21 96
        $this->libxmlDisableEntityLoader = $this->identifyLibxmlDisableEntityLoaderAvailability();
22
23 96
        if ($this->libxmlDisableEntityLoader) {
24 96
            libxml_disable_entity_loader(true);
25
        }
26 96
    }
27
28 96
    private function identifyLibxmlDisableEntityLoaderAvailability()
29
    {
30 96
        if (PHP_MAJOR_VERSION == 7) {
31 96
            switch (PHP_MINOR_VERSION) {
32 96
                case 2:
33 96
                    return PHP_RELEASE_VERSION >= 1;
34
                case 1:
35
                    return PHP_RELEASE_VERSION >= 13;
36
                case 0:
37
                    return PHP_RELEASE_VERSION >= 27;
38
            }
39
40
            return true;
41
        }
42
43
        return false;
44
    }
45
46
    /**
47
     * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
48
     *
49
     * @param mixed $xml
50
     *
51
     * @throws Exception
52
     *
53
     * @return string
54
     */
55 77
    public function scan($xml)
56
    {
57 77
        $pattern = '/encoding="(.*?)"/';
58 77
        $result = preg_match($pattern, $xml, $matches);
59 77
        $charset = $result ? $matches[1] : 'UTF-8';
60
61 77
        if ($charset !== 'UTF-8') {
62 1
            $xml = mb_convert_encoding($xml, 'UTF-8', $charset);
63
        }
64
65
        // Don't rely purely on libxml_disable_entity_loader()
66 77
        $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
67 77
        if (preg_match($pattern, $xml)) {
68 5
            throw new Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
69
        }
70
71 72
        return $xml;
72
    }
73
74
    /**
75
     * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
76
     *
77
     * @param string $filestream
78
     *
79
     * @throws Exception
80
     *
81
     * @return string
82
     */
83 20
    public function scanFile($filestream)
84
    {
85 20
        return $this->scan(file_get_contents($filestream));
86
    }
87
}
88