PharExtensionInterceptor.php 2.4 KB

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