diff --git a/src/MageScan/Command/Scan/AbstractCommand.php b/src/MageScan/Command/Scan/AbstractCommand.php new file mode 100644 index 0000000..1076f04 --- /dev/null +++ b/src/MageScan/Command/Scan/AbstractCommand.php @@ -0,0 +1,156 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Request; +use MageScan\Url; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Abstract scan command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +abstract class AbstractCommand extends Command +{ + /** + * Input object + * + * @var \Symfony\Component\Console\Input\InputInterface + */ + protected $input; + + /** + * Output object + * + * @var \Symfony\Component\Console\Output\OutputInterface + */ + protected $output; + + /** + * URL of Magento site + * + * @var string + */ + protected $url; + + /** + * Cached request object with desired secure flag + * + * @var \MageScan\Request + */ + protected $request; + + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->addArgument( + 'url', + InputArgument::REQUIRED, + 'The URL of the Magento application' + ) + ->addOption( + 'insecure', + 'k', + InputOption::VALUE_NONE, + 'Don\'t validate SSL certificate if URL is https' + ); + } + + /** + * Initialize command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->output = $output; + $this->request = new Request; + $this->request->setInsecure($this->input->getOption('insecure')); + + $style = new OutputFormatterStyle('white', 'blue', array('bold')); + $this->output->getFormatter()->setStyle('header', $style); + + $this->setUrl($input->getArgument('url')); + } + + /** + * Validate and set url + * + * @param string $input + * + * @return void + * @throws InvalidArgumentException + */ + protected function setUrl($input) + { + if (empty(trim($input))) { + throw new \InvalidArgumentException( + 'Target URL not specified' + ); + } + $url = new Url; + $this->url = $url->clean($input); + $request = $this->request; + $response = $request->fetch($this->url, array( + CURLOPT_NOBODY => true + )); + if ($response->code == 0) { + throw new \InvalidArgumentException( + 'Could not connect to URL: ' . $this->url + ); + } + if (isset($response->header['Location'])) { + $this->url = $response->header['Location']; + } + } + + /** + * Write a header block + * + * @param string $text + * @param string $style + * + * @return void + */ + protected function writeHeader($text, $style = 'bg=blue;fg=white') + { + $this->output->writeln(array( + '', + $this->getHelperSet()->get('formatter') + ->formatBlock($text, $style, true), + '', + )); + } +} diff --git a/src/MageScan/Command/Scan/AllCommand.php b/src/MageScan/Command/Scan/AllCommand.php new file mode 100644 index 0000000..0f4c65d --- /dev/null +++ b/src/MageScan/Command/Scan/AllCommand.php @@ -0,0 +1,85 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Run all scan commands + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class AllCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:all') + ->setDescription('Run all scans') + ->addOption( + 'show-modules', + null, + InputOption::VALUE_NONE, + 'Show all modules that were scanned for, not just matches' + ); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output->writeln('Scanning ' . $this->url . '...'); + foreach ([ + 'scan:version', + 'scan:module', + 'scan:catalog', + 'scan:patch', + 'scan:sitemap', + 'scan:server', + 'scan:unreachable', + ] as $commandName) { + $command = $this->getApplication()->find($commandName); + $args = [ + 'command' => $commandName, + 'url' => $input->getArgument('url') + ]; + if ($commandName === 'scan:module' && $input->getOption('show-modules')) { + $args['--show-modules'] = true; + } + $command->run(new ArrayInput($args), $output); + } + } +} diff --git a/src/MageScan/Command/Scan/CatalogCommand.php b/src/MageScan/Command/Scan/CatalogCommand.php new file mode 100644 index 0000000..4f3e4c0 --- /dev/null +++ b/src/MageScan/Command/Scan/CatalogCommand.php @@ -0,0 +1,77 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\Catalog; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan catalog command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class CatalogCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:catalog') + ->setDescription('Get catalog information'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Catalog Information'); + $rows = array(); + $catalog = new Catalog; + $catalog->setRequest($this->request); + $categoryCount = $catalog->categoryCount($this->url); + $rows[] = array( + 'Categories', + $categoryCount !== false ? $categoryCount : 'Unknown' + ); + $productCount = $catalog->productCount($this->url); + $rows[] = array( + 'Products', + $productCount !== false ? $productCount : 'Unknown' + ); + $table = new Table($this->output); + $table + ->setHeaders(array('Type', 'Count')) + ->setRows($rows) + ->render(); + } +} diff --git a/src/MageScan/Command/Scan/ModuleCommand.php b/src/MageScan/Command/Scan/ModuleCommand.php new file mode 100644 index 0000000..51e07bd --- /dev/null +++ b/src/MageScan/Command/Scan/ModuleCommand.php @@ -0,0 +1,89 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\Module; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan module command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class ModuleCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:modules') + ->setDescription('Get installed modules') + ->addOption( + 'show-modules', + null, + InputOption::VALUE_NONE, + 'Show all modules that were scanned for, not just matches' + ); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Installed Modules'); + $all = $input->getOption('show-modules'); + $module = new Module; + $module->setRequest($this->request); + $found = $notFound = array(); + foreach ($module->checkForModules($this->url) as $name => $exists) { + if ($exists) { + $found[] = array($name, 'Yes'); + } else { + $notFound[] = array($name, 'No'); + } + } + if (empty($found) && !$all) { + $this->output->writeln('No detectable modules were found'); + return; + } + if ($all) { + $found = array_merge($found, $notFound); + } + $table = new Table($this->output); + $table + ->setHeaders(array('Module', 'Installed')) + ->setRows($found) + ->render(); + } +} diff --git a/src/MageScan/Command/Scan/PatchCommand.php b/src/MageScan/Command/Scan/PatchCommand.php new file mode 100644 index 0000000..500a17a --- /dev/null +++ b/src/MageScan/Command/Scan/PatchCommand.php @@ -0,0 +1,84 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\Patch; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan path command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class PatchCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:patch') + ->setDescription('Get patch information'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Patches'); + $rows = array(); + $patch = new Patch; + $patch->setRequest($this->request); + $patches = $patch->checkAll($this->url); + foreach ($patches as $name => $result) { + switch ($result) { + case PATCH::PATCHED: + $status = 'Patched'; + break; + case PATCH::UNPATCHED: + $status = 'Unpatched'; + break; + default: + $status = 'Unknown'; + } + $rows[] = array( + $name, + $status + ); + } + $table = new Table($this->output); + $table + ->setHeaders(array('Name', 'Status')) + ->setRows($rows) + ->render(); + } +} diff --git a/src/MageScan/Command/Scan/ServerCommand.php b/src/MageScan/Command/Scan/ServerCommand.php new file mode 100644 index 0000000..f687caf --- /dev/null +++ b/src/MageScan/Command/Scan/ServerCommand.php @@ -0,0 +1,75 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\TechHeader; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan server tech command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class ServerCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:server') + ->setDescription('Check server technology'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Server Technology'); + $techHeader = new TechHeader; + $techHeader->setRequest($this->request); + $values = $techHeader->getHeaders($this->url); + if (empty($values)) { + $this->output->writeln('No detectable technology was found'); + return; + } + $rows = array(); + foreach ($values as $key => $value) { + $rows[] = array($key, $value); + } + $table = new Table($this->output); + $table + ->setHeaders(array('Key', 'Value')) + ->setRows($rows) + ->render(); + } +} diff --git a/src/MageScan/Command/Scan/SitemapCommand.php b/src/MageScan/Command/Scan/SitemapCommand.php new file mode 100644 index 0000000..a74186e --- /dev/null +++ b/src/MageScan/Command/Scan/SitemapCommand.php @@ -0,0 +1,95 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\Sitemap; +use MageScan\Request; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan sitemap command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class SitemapCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:sitemap') + ->setDescription('Check sitemap'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Sitemap'); + $url = $this->getSitemapUrl(); + $request = new Request; + $response = $request->fetch($url, array( + CURLOPT_NOBODY => true, + CURLOPT_FOLLOWLOCATION => true + )); + if ($response->code == 200) { + $this->output + ->writeln('Sitemap is accessible: ' . $url); + } else { + $this->output + ->writeln('Sitemap is not accessible: ' . $url); + } + } + + /** + * Parse the robots.txt text file to find the sitemap + * + * @return string + */ + protected function getSitemapUrl() + { + $request = new Request; + $response = $request->fetch($this->url . 'robots.txt'); + $sitemap = new Sitemap; + $sitemap->setRequest($this->request); + $sitemap = $sitemap->getSitemapFromRobotsTxt($response); + if ($sitemap === false) { + $this->output->writeln( + 'Sitemap is not declared in robots.txt' + ); + return $this->url . 'sitemap.xml'; + } + $this->output + ->writeln('Sitemap is declared in robots.txt'); + return (string) $sitemap; + } +} diff --git a/src/MageScan/Command/Scan/UnreachableCommand.php b/src/MageScan/Command/Scan/UnreachableCommand.php new file mode 100644 index 0000000..56a78ca --- /dev/null +++ b/src/MageScan/Command/Scan/UnreachableCommand.php @@ -0,0 +1,74 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\UnreachablePath; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan unreachable path command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class UnreachableCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:unreachable') + ->setDescription('Check unreachable paths'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Unreachable Path Check'); + $unreachablePath = new UnreachablePath; + $unreachablePath->setRequest($this->request); + $results = $unreachablePath->checkPaths($this->url); + foreach ($results as &$result) { + if ($result[2] === false) { + $result[2] = 'Fail'; + } elseif ($result[2] === true) { + $result[2] = 'Pass'; + } + } + $table = new Table($this->output); + $table + ->setHeaders(array('Path', 'Response Code', 'Status')) + ->setRows($results) + ->render(); + } +} diff --git a/src/MageScan/Command/Scan/VersionCommand.php b/src/MageScan/Command/Scan/VersionCommand.php new file mode 100644 index 0000000..3f71a85 --- /dev/null +++ b/src/MageScan/Command/Scan/VersionCommand.php @@ -0,0 +1,71 @@ + + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ + +namespace MageScan\Command\Scan; + +use MageScan\Check\Version; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Scan version command + * + * @category MageScan + * @package MageScan + * @author Steve Robbins + * @copyright 2015 Steve Robbins + * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 + * @link https://github.com/steverobbins/magescan + */ +class VersionCommand extends AbstractCommand +{ + /** + * Configure command + * + * @return void + */ + protected function configure() + { + $this + ->setName('scan:version') + ->setDescription('Get the version of a Magento installation'); + parent::configure(); + } + + /** + * Execute command + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->writeHeader('Magento Information'); + $version = new Version; + $version->setRequest($this->request); + $version = $version->getInfo($this->url); + $rows = array( + array('Edition', $version[0] ?: 'Unknown'), + array('Version', $version[1] ?: 'Unknown') + ); + $table = new Table($this->output); + $table + ->setHeaders(array('Parameter', 'Value')) + ->setRows($rows) + ->render(); + } +} diff --git a/src/MageScan/Command/ScanCommand.php b/src/MageScan/Command/ScanCommand.php deleted file mode 100644 index 2481d7b..0000000 --- a/src/MageScan/Command/ScanCommand.php +++ /dev/null @@ -1,392 +0,0 @@ - - * @copyright 2015 Steve Robbins - * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 - * @link https://github.com/steverobbins/magescan - */ - -namespace MageScan\Command; - -use MageScan\Check\Catalog; -use MageScan\Check\Module; -use MageScan\Check\Patch; -use MageScan\Check\Sitemap; -use MageScan\Check\TechHeader; -use MageScan\Check\UnreachablePath; -use MageScan\Check\Version; -use MageScan\Request; -use MageScan\Url; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; -use Symfony\Component\Console\Helper\Table; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Scan a Magento site using PHP CLI - * - * @category MageScan - * @package MageScan - * @author Steve Robbins - * @copyright 2015 Steve Robbins - * @license http://creativecommons.org/licenses/by/4.0/ CC BY 4.0 - * @link https://github.com/steverobbins/magescan - */ -class ScanCommand extends Command -{ - /** - * Input object - * - * @var \Symfony\Component\Console\Input\InputInterface - */ - protected $input; - - /** - * Output object - * - * @var \Symfony\Component\Console\Output\OutputInterface - */ - protected $output; - - /** - * URL of Magento site - * - * @var string - */ - protected $url; - - /** - * Cached request object with desired secure flag - * - * @var \MageScan\Request - */ - protected $request; - - /** - * Configure scan command - * - * @return void - */ - protected function configure() - { - $this - ->setName('scan') - ->setDescription('Audit a Magento site as best you can by URL') - ->addArgument( - 'url', - InputArgument::REQUIRED, - 'The URL of the Magento application' - ) - ->addOption( - 'show-modules', - null, - InputOption::VALUE_NONE, - 'Show all modules that were scanned for, not just matches' - ) - ->addOption( - 'insecure', - 'k', - InputOption::VALUE_NONE, - 'Don\'t validate SSL certificate if URL is https' - ); - } - - /** - * Run scan command - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return void - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->input = $input; - $this->output = $output; - $this->request = new Request; - $this->request->setInsecure($this->input->getOption('insecure')); - - $style = new OutputFormatterStyle('white', 'blue', array('bold')); - $this->output->getFormatter()->setStyle('header', $style); - - $this->setUrl($input->getArgument('url')); - $this->output->writeln('Scanning ' . $this->url . '...'); - - $this->checkMagentoInfo(); - $this->checkModules($input->getOption('show-modules')); - $this->checkCatalog(); - $this->checkPatch(); - $this->checkSitemapExists(); - $this->checkServerTech(); - $this->checkUnreachablePath(); - } - - /** - * Get information about the Magento application - * - * @return void - */ - protected function checkMagentoInfo() - { - $this->writeHeader('Magento Information'); - $version = new Version; - $version->setRequest($this->request); - $version = $version->getInfo($this->url); - $rows = array( - array('Edition', $version[0] ?: 'Unknown'), - array('Version', $version[1] ?: 'Unknown') - ); - $table = new Table($this->output); - $table - ->setHeaders(array('Parameter', 'Value')) - ->setRows($rows) - ->render(); - } - - /** - * Check for files known to be associated with a module - * - * @param boolean $all - * - * @return void - */ - protected function checkModules($all = false) - { - $this->writeHeader('Installed Modules'); - $module = new Module; - $module->setRequest($this->request); - $found = $notFound = array(); - foreach ($module->checkForModules($this->url) as $name => $exists) { - if ($exists) { - $found[] = array($name, 'Yes'); - } else { - $notFound[] = array($name, 'No'); - } - } - if (empty($found) && !$all) { - $this->output->writeln('No detectable modules were found'); - return; - } - if ($all) { - $found = array_merge($found, $notFound); - } - $table = new Table($this->output); - $table - ->setHeaders(array('Module', 'Installed')) - ->setRows($found) - ->render(); - } - - /** - * Get catalog data - * - * @return void - */ - protected function checkCatalog() - { - $this->writeHeader('Catalog Information'); - $rows = array(); - $catalog = new Catalog; - $catalog->setRequest($this->request); - $categoryCount = $catalog->categoryCount($this->url); - $rows[] = array( - 'Categories', - $categoryCount !== false ? $categoryCount : 'Unknown' - ); - $productCount = $catalog->productCount($this->url); - $rows[] = array( - 'Products', - $productCount !== false ? $productCount : 'Unknown' - ); - $table = new Table($this->output); - $table - ->setHeaders(array('Type', 'Count')) - ->setRows($rows) - ->render(); - } - - /** - * Check for installed patches - * - * @return void - */ - protected function checkPatch() - { - $this->writeHeader('Patches'); - $rows = array(); - $patch = new Patch; - $patch->setRequest($this->request); - $patches = $patch->checkAll($this->url); - foreach ($patches as $name => $result) { - switch ($result) { - case PATCH::PATCHED: - $status = 'Patched'; - break; - case PATCH::UNPATCHED: - $status = 'Unpatched'; - break; - default: - $status = 'Unknown'; - } - $rows[] = array( - $name, - $status - ); - } - $table = new Table($this->output); - $table - ->setHeaders(array('Name', 'Status')) - ->setRows($rows) - ->render(); - } - - /** - * Check HTTP status codes for files/paths that shouldn't be reachable - * - * @return void - */ - protected function checkUnreachablePath() - { - $this->writeHeader('Unreachable Path Check'); - $unreachablePath = new UnreachablePath; - $unreachablePath->setRequest($this->request); - $results = $unreachablePath->checkPaths($this->url); - foreach ($results as &$result) { - if ($result[2] === false) { - $result[2] = 'Fail'; - } elseif ($result[2] === true) { - $result[2] = 'Pass'; - } - } - $table = new Table($this->output); - $table - ->setHeaders(array('Path', 'Response Code', 'Status')) - ->setRows($results) - ->render(); - } - - /** - * Analize the server technology being used - * - * @return void - */ - protected function checkServerTech() - { - $this->writeHeader('Server Technology'); - $techHeader = new TechHeader; - $techHeader->setRequest($this->request); - $values = $techHeader->getHeaders($this->url); - if (empty($values)) { - $this->output->writeln('No detectable technology was found'); - return; - } - $rows = array(); - foreach ($values as $key => $value) { - $rows[] = array($key, $value); - } - $table = new Table($this->output); - $table - ->setHeaders(array('Key', 'Value')) - ->setRows($rows) - ->render(); - } - - /** - * Check that the store is correctly using a sitemap - * - * @return void - */ - protected function checkSitemapExists() - { - $this->writeHeader('Sitemap'); - $url = $this->getSitemapUrl(); - $request = new Request; - $response = $request->fetch($url, array( - CURLOPT_NOBODY => true, - CURLOPT_FOLLOWLOCATION => true - )); - if ($response->code == 200) { - $this->output - ->writeln('Sitemap is accessible: ' . $url); - } else { - $this->output - ->writeln('Sitemap is not accessible: ' . $url); - } - } - - /** - * Parse the robots.txt text file to find the sitemap - * - * @return string - */ - protected function getSitemapUrl() - { - $request = new Request; - $response = $request->fetch($this->url . 'robots.txt'); - $sitemap = new Sitemap; - $sitemap->setRequest($this->request); - $sitemap = $sitemap->getSitemapFromRobotsTxt($response); - if ($sitemap === false) { - $this->output->writeln( - 'Sitemap is not declared in robots.txt' - ); - return $this->url . 'sitemap.xml'; - } - $this->output - ->writeln('Sitemap is declared in robots.txt'); - return (string) $sitemap; - } - - /** - * Validate and set url - * - * @param string $input - * - * @return void - * @throws InvalidArgumentException - */ - protected function setUrl($input) - { - $url = new Url; - $this->url = $url->clean($input); - $request = $this->request; - $response = $request->fetch($this->url, array( - CURLOPT_NOBODY => true - )); - if ($response->code == 0) { - throw new \InvalidArgumentException( - 'Could not connect to URL: ' . $this->url - ); - } - if (isset($response->header['Location'])) { - $this->url = $response->header['Location']; - } - } - - /** - * Write a header block - * - * @param string $text - * @param string $style - * - * @return void - */ - protected function writeHeader($text, $style = 'bg=blue;fg=white') - { - $this->output->writeln(array( - '', - $this->getHelperSet()->get('formatter') - ->formatBlock($text, $style, true), - '', - )); - } -} diff --git a/src/bootstrap.php b/src/bootstrap.php index 75b09e5..c5ba477 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -12,15 +12,31 @@ * @link https://github.com/steverobbins/magescan */ +ini_set('display_errors', 1); + require_once __DIR__ . '/../vendor/autoload.php'; -use MageScan\Command\ScanCommand; +use MageScan\Command\Scan\AllCommand; +use MageScan\Command\Scan\CatalogCommand; +use MageScan\Command\Scan\ModuleCommand; +use MageScan\Command\Scan\PatchCommand; +use MageScan\Command\Scan\ServerCommand; +use MageScan\Command\Scan\SitemapCommand; +use MageScan\Command\Scan\VersionCommand; +use MageScan\Command\Scan\UnreachableCommand; use MageScan\Command\SelfUpdateCommand; use Symfony\Component\Console\Application; $app = new Application('Mage Scan', '1.9.2'); -$app->add(new ScanCommand); +$app->add(new AllCommand); +$app->add(new VersionCommand); +$app->add(new ModuleCommand); +$app->add(new CatalogCommand); +$app->add(new PatchCommand); +$app->add(new SitemapCommand); +$app->add(new ServerCommand); +$app->add(new UnreachableCommand); $app->add(new SelfUpdateCommand); $app->run(); diff --git a/test/MGA/Command/ScanCommandTest.php b/test/MGA/Command/ScanAllCommandTest.php similarity index 92% rename from test/MGA/Command/ScanCommandTest.php rename to test/MGA/Command/ScanAllCommandTest.php index f6dcc92..bd81cea 100644 --- a/test/MGA/Command/ScanCommandTest.php +++ b/test/MGA/Command/ScanAllCommandTest.php @@ -11,7 +11,14 @@ namespace MageScan\Test\Command; -use MageScan\Command\ScanCommand; +use MageScan\Command\Scan\AllCommand; +use MageScan\Command\Scan\CatalogCommand; +use MageScan\Command\Scan\ModuleCommand; +use MageScan\Command\Scan\PatchCommand; +use MageScan\Command\Scan\ServerCommand; +use MageScan\Command\Scan\SitemapCommand; +use MageScan\Command\Scan\VersionCommand; +use MageScan\Command\Scan\UnreachableCommand; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Console\Application; use PHPUnit_Framework_TestCase; @@ -19,17 +26,24 @@ /** * Run tests on the scan command */ -class ScanCommandTest extends PHPUnit_Framework_TestCase +class ScanAllCommandTest extends PHPUnit_Framework_TestCase { public function testExecute() { - $application = new Application(); - $application->add(new ScanCommand()); - - $command = $application->find('scan'); + $application = new Application; + $application->add(new AllCommand); + $application->add(new CatalogCommand); + $application->add(new ModuleCommand); + $application->add(new PatchCommand); + $application->add(new ServerCommand); + $application->add(new SitemapCommand); + $application->add(new VersionCommand); + $application->add(new UnreachableCommand); + + $command = $application->find('scan:all'); $commandTester = new CommandTester($command); $commandTester->execute(array( - 'command' => $command->getName(), + 'command' => 'scan:all', 'url' => 'enterprise-demo.user.magentotrial.com' )); $display = <<