diff --git a/bin/grav b/bin/grav
index faa8846f7..e36f0cc1c 100755
--- a/bin/grav
+++ b/bin/grav
@@ -41,5 +41,6 @@ $app->addCommands(array(
new \Grav\Console\Cli\ClearCacheCommand(),
new \Grav\Console\Cli\BackupCommand(),
new \Grav\Console\Cli\NewProjectCommand(),
+ new \Grav\Console\Cli\SecurityCommand(),
));
$app->run();
diff --git a/system/src/Grav/Common/Security.php b/system/src/Grav/Common/Security.php
index b2f647110..9b345cb18 100644
--- a/system/src/Grav/Common/Security.php
+++ b/system/src/Grav/Common/Security.php
@@ -11,24 +11,32 @@ namespace Grav\Common;
class Security
{
- public static function detectXssFromPages($pages, callable $messager = null)
+ public static function detectXssFromPages($pages, callable $status = null)
{
$routes = $pages->routes();
+ // Remove duplicate for homepage
+ unset($routes['/']);
+
$list = [];
+// // This needs Symfony 4.1 to work
+// $status && $status([
+// 'type' => 'count',
+// 'steps' => count($routes),
+// ]);
+
foreach ($routes as $route => $path) {
- $messager && $messager([
+ $status && $status([
'type' => 'progress',
- 'percentage' => false,
- 'complete' => false
]);
try {
$page = $pages->get($path);
+
// call the content to load/cache it
- $header = $page->header();
+ $header = (array) $page->header();
$content = $page->content();
$data = ['header' => $header, 'content' => $content];
@@ -43,7 +51,7 @@ class Security
}
}
- dump($list);
+ return $list;
}
/**
diff --git a/system/src/Grav/Console/Cli/SecurityCommand.php b/system/src/Grav/Console/Cli/SecurityCommand.php
index 5e3568447..3361d44ce 100644
--- a/system/src/Grav/Console/Cli/SecurityCommand.php
+++ b/system/src/Grav/Console/Cli/SecurityCommand.php
@@ -13,12 +13,10 @@ use Grav\Common\Security;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Style\SymfonyStyle;
class SecurityCommand extends ConsoleCommand
{
- /** @var string $source */
- protected $source;
-
/** @var ProgressBar $progress */
protected $progress;
@@ -29,13 +27,7 @@ class SecurityCommand extends ConsoleCommand
{
$this
->setName("security")
- ->addArgument(
- 'xss',
- InputArgument::OPTIONAL,
- 'Perform XSS security checks on all pages'
-
- )
- ->setDescription("Capable of running XSS Security checks")
+ ->setDescription("Capable of running various Security checks")
->setHelp('The security runs various security checks on your Grav site');
$this->source = getcwd();
@@ -46,34 +38,69 @@ class SecurityCommand extends ConsoleCommand
*/
protected function serve()
{
- $this->progress = new ProgressBar($this->output);
- $this->progress->setFormat('Archiving %current% files [%bar%] %elapsed:6s% %memory:6s%');
+
/** @var Grav $grav */
$grav = Grav::instance();
+ $grav['uri']->init();
$grav['config']->init();
$grav['debugger']->enabled(false);
+ $grav['streams'];
+ $grav['plugins']->init();
+ $grav['themes']->init();
+
+
$grav['twig']->init();
$grav['pages']->init();
- $results = Security::detectXssFromPages($grav['pages'], [$this, 'output']);
+ $this->progress = new ProgressBar($this->output, (count($grav['pages']->routes()) - 1));
+ $this->progress->setFormat('Scanning %current% pages [%bar%] %percent:3s%% %elapsed:6s%');
+ $this->progress->setBarWidth(100);
- print_r($results);
+ $io = new SymfonyStyle($this->input, $this->output);
+ $io->title('Grav Security Check');
+
+ $output = Security::detectXssFromPages($grav['pages'], [$this, 'outputProgress']);
+
+ $io->newline(2);
+
+ if (!empty($output)) {
+
+ $counter = 1;
+ foreach ($output as $route => $results) {
+
+ $results_parts = array_map(function($value, $key) {
+ return $key.': \''.$value . '\'';
+ }, array_values($results), array_keys($results));
+
+ $io->writeln($counter++ .' - ' . $route . ' → ' . implode(', ', $results_parts) . '');
+ }
+
+ $io->error('Security Scan complete: ' . count($output) . ' potential XSS issues found...');
+
+ } else {
+ $io->success('Security Scan complete: No issues found...');
+ }
+
+ $io->newline(1);
}
/**
* @param $args
*/
- public function output($args)
+ public function outputProgress($args)
{
switch ($args['type']) {
- case 'message':
- $this->output->writeln($args['message']);
+ case 'count':
+ $steps = $args['steps'];
+ $freq = intval($steps > 100 ? round($steps / 100) : $steps);
+ $this->progress->setMaxSteps($steps);
+ $this->progress->setRedrawFrequency($freq);
break;
case 'progress':
- if ($args['complete']) {
+ if (isset($args['complete']) && $args['complete']) {
$this->progress->finish();
} else {
$this->progress->advance();