SecurityCommand.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. /**
  3. * @package Grav\Console\Cli
  4. *
  5. * @copyright Copyright (c) 2015 - 2023 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Console\Cli;
  9. use Grav\Common\Grav;
  10. use Grav\Common\Security;
  11. use Grav\Console\GravCommand;
  12. use Symfony\Component\Console\Helper\ProgressBar;
  13. use function count;
  14. /**
  15. * Class SecurityCommand
  16. * @package Grav\Console\Cli
  17. */
  18. class SecurityCommand extends GravCommand
  19. {
  20. /** @var ProgressBar $progress */
  21. protected $progress;
  22. /**
  23. * @return void
  24. */
  25. protected function configure(): void
  26. {
  27. $this
  28. ->setName('security')
  29. ->setDescription('Capable of running various Security checks')
  30. ->setHelp('The <info>security</info> runs various security checks on your Grav site');
  31. }
  32. /**
  33. * @return int
  34. */
  35. protected function serve(): int
  36. {
  37. $this->initializePages();
  38. $io = $this->getIO();
  39. /** @var Grav $grav */
  40. $grav = Grav::instance();
  41. $this->progress = new ProgressBar($this->output, count($grav['pages']->routes()) - 1);
  42. $this->progress->setFormat('Scanning <cyan>%current%</cyan> pages [<green>%bar%</green>] <white>%percent:3s%%</white> %elapsed:6s%');
  43. $this->progress->setBarWidth(100);
  44. $io->title('Grav Security Check');
  45. $io->newline(2);
  46. $output = Security::detectXssFromPages($grav['pages'], false, [$this, 'outputProgress']);
  47. $error = 0;
  48. if (!empty($output)) {
  49. $counter = 1;
  50. foreach ($output as $route => $results) {
  51. $results_parts = array_map(static function ($value, $key) {
  52. return $key.': \''.$value . '\'';
  53. }, array_values($results), array_keys($results));
  54. $io->writeln($counter++ .' - <cyan>' . $route . '</cyan> → <red>' . implode(', ', $results_parts) . '</red>');
  55. }
  56. $error = 1;
  57. $io->error('Security Scan complete: ' . count($output) . ' potential XSS issues found...');
  58. } else {
  59. $io->success('Security Scan complete: No issues found...');
  60. }
  61. $io->newline(1);
  62. return $error;
  63. }
  64. /**
  65. * @param array $args
  66. * @return void
  67. */
  68. public function outputProgress(array $args): void
  69. {
  70. switch ($args['type']) {
  71. case 'count':
  72. $steps = $args['steps'];
  73. $freq = (int)($steps > 100 ? round($steps / 100) : $steps);
  74. $this->progress->setMaxSteps($steps);
  75. $this->progress->setRedrawFrequency($freq);
  76. break;
  77. case 'progress':
  78. if (isset($args['complete']) && $args['complete']) {
  79. $this->progress->finish();
  80. } else {
  81. $this->progress->advance();
  82. }
  83. break;
  84. }
  85. }
  86. }