Commit | Line | Data |
---|---|---|
2d38c687 PF |
1 | <?php |
2 | ||
3 | namespace Civi\Core\Security; | |
4 | ||
5 | use TYPO3\PharStreamWrapper\Assertable; | |
6 | use TYPO3\PharStreamWrapper\Helper; | |
7 | use TYPO3\PharStreamWrapper\Exception; | |
8 | ||
9 | /** | |
10 | * An alternate PharExtensionInterceptor to support phar-based CLI tools. | |
11 | * | |
12 | * This is largely based on Drupal\Core\Security\PharExtensionInterceptor, | |
13 | * originally licensed under GPL2+ | |
14 | * | |
15 | * @see \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor | |
16 | */ | |
17 | class PharExtensionInterceptor implements Assertable { | |
18 | ||
19 | /** | |
20 | * Determines whether phar file is allowed to execute. | |
21 | * | |
22 | * The phar file is allowed to execute if: | |
23 | * - the base file name has a ".phar" suffix. | |
24 | * - it is the CLI tool that has invoked the interceptor. | |
25 | * | |
26 | * @param string $path | |
27 | * The path of the phar file to check. | |
28 | * @param string $command | |
29 | * The command being carried out. | |
30 | * | |
31 | * @return bool | |
32 | * TRUE if the phar file is allowed to execute. | |
33 | * | |
34 | * @throws Exception | |
35 | * Thrown when the file is not allowed to execute. | |
36 | */ | |
37 | public function assert(string $path, string $command): bool { | |
38 | if ($this->baseFileContainsPharExtension($path)) { | |
39 | return TRUE; | |
40 | } | |
41 | throw new Exception( | |
42 | sprintf( | |
43 | 'Unexpected file extension in "%s"', | |
44 | $path | |
45 | ), | |
46 | 1535198703 | |
47 | ); | |
48 | } | |
49 | ||
50 | /** | |
51 | * Determines if a path has a .phar extension or invoked execution. | |
52 | * | |
53 | * @param string $path | |
54 | * The path of the phar file to check. | |
55 | * | |
56 | * @return bool | |
57 | * TRUE if the file has a .phar extension or if the execution has been | |
58 | * invoked by the phar file. | |
59 | */ | |
60 | private function baseFileContainsPharExtension($path) { | |
61 | $baseFile = Helper::determineBaseFile($path); | |
62 | if ($baseFile === NULL) { | |
63 | return FALSE; | |
64 | } | |
65 | // If the stream wrapper is registered by invoking a phar file that does | |
66 | // not not have .phar extension then this should be allowed. For | |
67 | // example, some CLI tools recommend removing the extension. | |
68 | $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); | |
69 | // Find the last entry in the backtrace containing a 'file' key as | |
70 | // sometimes the last caller is executed outside the scope of a file. For | |
71 | // example, this occurs with shutdown functions. | |
72 | do { | |
73 | $caller = array_pop($backtrace); | |
74 | } while (empty($caller['file']) && !empty($backtrace)); | |
75 | if (isset($caller['file']) && $baseFile === Helper::determineBaseFile($caller['file'])) { | |
76 | return TRUE; | |
77 | } | |
78 | $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION); | |
79 | return strtolower($fileExtension) === 'phar'; | |
80 | } | |
81 | ||
82 | } |