Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.52% covered (warning)
56.52%
13 / 23
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PathChecker
56.52% covered (warning)
56.52%
13 / 23
20.00% covered (danger)
20.00%
1 / 5
30.11
0.00% covered (danger)
0.00%
0 / 1
 checkAbsolutePath
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
6.00
 checkAbsolutePathAndExists
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
4.37
 checkSourcePath
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkDestinationPath
57.14% covered (warning)
57.14%
4 / 7
0.00% covered (danger)
0.00%
0 / 1
5.26
 checkSourceAndDestinationPaths
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace WebPConvert\Helpers;
4
5use WebPConvert\Exceptions\InvalidInputException;
6use WebPConvert\Exceptions\InvalidInput\TargetNotFoundException;
7
8/**
9 * Functions for sanitizing.
10 *
11 * @package    WebPConvert
12 * @author     Bjørn Rosell <it@rosell.dk>
13 * @since      Class available since Release 2.0.6
14 */
15class PathChecker
16{
17
18    /**
19     * Check absolute file path to prevent attacks.
20     *
21     * - Prevents non printable characters
22     * - Prevents stream wrappers
23     * - Prevents directory traversal
24     *
25     * Preventing non printable characters is especially done to prevent the NUL character, which can be used
26     * to bypass other tests. See https://st-g.de/2011/04/doing-filename-checks-securely-in-PHP.
27     *
28     * Preventeng stream wrappers is especially done to protect against Phar Deserialization.
29     * See https://blog.ripstech.com/2018/new-php-exploitation-technique/
30     *
31     * @param  string  $absFilePath
32     * @return void
33     */
34    public static function checkAbsolutePath($absFilePath, $text = 'file')
35    {
36        if (empty($absFilePath)) {
37            throw new InvalidInputException('Empty filepath for ' . $text);
38        }
39
40        // Prevent non printable characters
41        /*
42        if (!ctype_print($absFilePath)) {
43            throw new InvalidInputException('Non-printable characters are not allowed in ' . $text);
44        }*/
45
46        // Prevent control characters (at least the first 32 (#0 - #1f)
47        if (preg_match('#[\x{0}-\x{1f}]#', $absFilePath)) {
48            throw new InvalidInputException('Non-printable characters are not allowed');
49        }
50
51        // Prevent directory traversal
52        /* Disabled. We DO allow it again (#203)
53        if (preg_match('#\.\.\/#', $absFilePath)) {
54            throw new InvalidInputException('Directory traversal is not allowed in ' . $text . ' path');
55        }*/
56
57        // Prevent stream wrappers ("phar://", "php://" and the like)
58        // https://www.php.net/manual/en/wrappers.phar.php
59        if (preg_match('#^\\w+://#', $absFilePath)) {
60            throw new InvalidInputException('Stream wrappers are not allowed in ' . $text . ' path');
61        }
62    }
63
64    public static function checkAbsolutePathAndExists($absFilePath, $text = 'file')
65    {
66        if (empty($absFilePath)) {
67            throw new TargetNotFoundException($text . ' argument missing');
68        }
69        self::checkAbsolutePath($absFilePath, $text);
70        if (@!file_exists($absFilePath)) {
71            throw new TargetNotFoundException($text . ' file was not found');
72        }
73        if (@is_dir($absFilePath)) {
74            throw new InvalidInputException($text . ' is a directory');
75        }
76    }
77
78    /**
79     *  Checks that source path is secure, file exists and it is not a dir.
80     *
81     *  To also check mime type, use InputValidator::checkSource
82     */
83    public static function checkSourcePath($source)
84    {
85        self::checkAbsolutePathAndExists($source, 'source');
86    }
87
88    public static function checkDestinationPath($destination)
89    {
90        if (empty($destination)) {
91            throw new InvalidInputException('Destination argument missing');
92        }
93        self::checkAbsolutePath($destination, 'destination');
94
95        if (!preg_match('#\.webp$#i', $destination)) {
96            // Prevent overriding important files.
97            // Overriding an .htaccess file would lay down the website.
98            throw new InvalidInputException(
99                'Destination file must end with ".webp". ' .
100                'If you deliberately want to store the webp files with another extension, you must rename ' .
101                'the file after successful conversion'
102            );
103        }
104
105        if (@is_dir($destination)) {
106            throw new InvalidInputException('Destination is a directory');
107        }
108    }
109
110    public static function checkSourceAndDestinationPaths($source, $destination)
111    {
112        self::checkSourcePath($source);
113        self::checkDestinationPath($destination);
114    }
115}