diff --git a/Policies/d7_security_module_updates.policy.yml b/Policies/d7_security_module_updates.policy.yml new file mode 100644 index 0000000..9ccbf9a --- /dev/null +++ b/Policies/d7_security_module_updates.policy.yml @@ -0,0 +1,18 @@ +title: "Drupal 7 Security Module Updates policy" +class: \Drutiny\algm\Audit\D7SecurityModuleUpdates +name: algm:D7SecurityModuleUpdates +tags: + - Drupal 7 + - Security +description: | + It important to keep your site up to date and patched from known security vulnerabilities. + Note that upgrading modules, especially between major versions can introduce + regressions into your site. While its important to maintain a continual update + schedule for your site, regression testing changes is of equal importance. +success: No security updates were found. +failure: >- + {{ updates }} +warning: | + There are modules with available updates. Please consider upgrading as it + reduces the chance of introducing regressions when more urgent security updates + are required. diff --git a/Policies/security_module_updates.policy.yml b/Policies/d8_security_module_updates.policy.yml similarity index 64% rename from Policies/security_module_updates.policy.yml rename to Policies/d8_security_module_updates.policy.yml index 49f4180..44847c2 100644 --- a/Policies/security_module_updates.policy.yml +++ b/Policies/d8_security_module_updates.policy.yml @@ -1,10 +1,9 @@ -title: "Drupal Security Module Updates policy" -class: \Drutiny\algm\Audit\SecurityModuleUpdates -name: algm:SecurityModuleUpdates +title: "Drupal 8/9 Security Module Updates policy" +class: \Drutiny\algm\Audit\D8SecurityModuleUpdates +name: algm:D8SecurityModuleUpdates tags: - Drupal 9 - Drupal 8 - - Drupal 7 - Security description: | It important to keep your site up to date and patched from known security vulnerabilities. @@ -13,15 +12,7 @@ description: | schedule for your site, regression testing changes is of equal importance. success: No security updates were found. failure: >- - [{ - {{#updates}} - "title": "{{title}}", - "name": "{{ name }}", - "existing_version": "{{existing_version}}", - "recommended": "{{recommended}}", - "status_msg": "{{status_msg}}", - {{/updates}} - }] + {{ updates }} warning: | There are modules with available updates. Please consider upgrading as it reduces the chance of introducing regressions when more urgent security updates diff --git a/Policies/database_analysis.policy.yml b/Policies/database_analysis.policy.yml new file mode 100644 index 0000000..f70854f --- /dev/null +++ b/Policies/database_analysis.policy.yml @@ -0,0 +1,8 @@ +title: "Database Analysis" +class: \Drutiny\algm\Audit\DatabaseAnalysis +name: algm:DatabseAnalysis +description: | + Runs a databsae analysis to find out info about the database. +success: | + {{ database_stats }} +failure: Could not find database info. diff --git a/Profiles/algm_d7_sla_site.profile.yml b/Profiles/algm_d7_sla_site.profile.yml new file mode 100644 index 0000000..1195d2f --- /dev/null +++ b/Profiles/algm_d7_sla_site.profile.yml @@ -0,0 +1,45 @@ +title: 'ALGM Drupal 7 SLA audit' +description: 'This audit is for Drupal 7 sites which are under the ALGM SLA' +policies: + # ALGM specific + 'algm:HealthCheck': + { severity: high } + 'algm:DrushStatus': + { severity: normal } + 'algm:D7SecurityModuleUpdates': + { severity: high } + 'algm:FileSystemAnalysis': + { severity: normal } + # D7 + 'Drupal-7:NoDuplicateModules': { severity: normal } + 'Drupal-7:OverlayModuleDisabled': { severity: normal } + 'Drupal-7:BlackListPermissions': { severity: normal } + 'Drupal-7:PhpModuleDisabled': { severity: normal } + 'Drupal-7:SimpletestModuleDisabled': { severity: normal } + 'Drupal-7:StatisticsModuleDisabled': { severity: normal } + 'Drupal-7:UpdateModuleDisabled': { severity: normal } + 'Drupal-7:XMLSitemapBaseURL': { severity: normal } + 'Drupal-7:ZenRegistryRebuild': { severity: normal } + # FS and Database + 'fs:largeFiles': { severity: normal } + 'Drupal:largeFiles': { severity: normal } + 'Drupal:updates': + { severity: normal, + parameters: { + max_size: 1000, + warning_size: 250 + } + } + 'Database:Fulltext': { severity: normal } + 'Database:Size': { severity: normal } + # Security + 'Drupal-7:User1LockDown': { severity: normal } + 'fs:SensitivePublicFiles': + { + severity: high, + parameters: { + extensions: 'sql, sh, php, py, bz2, gz, tar, tgz, zip' + } + } +include: + - d7_security_review \ No newline at end of file diff --git a/Profiles/algm_sla_site.profile.yml b/Profiles/algm_sla_site.profile.yml index ec90142..ac18861 100644 --- a/Profiles/algm_sla_site.profile.yml +++ b/Profiles/algm_sla_site.profile.yml @@ -8,6 +8,8 @@ policies: { severity: normal } 'algm:D9ModuleUpdates': { severity: high } + 'algm:D8SecurityModuleUpdates': + { severity: high } 'algm:FileSystemAnalysis': { severity: normal } # D8 @@ -58,5 +60,4 @@ policies: } } include: - - securityheaders - d8_security_review \ No newline at end of file diff --git a/src/Audit/D7SecurityModuleUpdates.php b/src/Audit/D7SecurityModuleUpdates.php new file mode 100644 index 0000000..48055ca --- /dev/null +++ b/src/Audit/D7SecurityModuleUpdates.php @@ -0,0 +1,67 @@ +exec('drush pm-updatestatus --security-only --full --format=json'); + } + catch (Exception $e) { + throw new \Exception("Drush 8 command failed"); + return Audit::ERROR; + } + + if ($modules === '') { + $sandbox->setParameter('updates', 'No security modules to update.'); + return Audit::SUCCESS; + } + + $modules = json_decode($modules, TRUE); + if ($modules === null) { + return AUDIT::ERROR; + } + + $results = array_map(function($module) { + return([ + 'name' => isset($module['name']) ? $module['name'] : '', + 'existing_version' => isset($module['existing_version']) ? $module['existing_version'] : '', + 'latest_version' => isset($module['latest_version']) ? $module['latest_version'] : '', + 'recommended' => isset($module['recommended']) ? $module['recommended'] : '', + 'status_msg' => isset($module['status_msg']) ? $module['status_msg'] : '', + 'link' => isset($module['link']) ? $module['link'] : '', + ]); + }, $modules); + + $columns = ['Name', 'Current Version', 'Recommended', 'Status', 'Link']; + $rows = []; + foreach ($results as $key => $m) { + $rows[] = [ $m["name"], $m["existing_version"], $m["recommended"], $m['status_msg'], $m['link'] ]; + } + + $md_table = new MarkdownTableGenerator($columns, $rows); + $rendered_table_markdown = $md_table->render(); + + $sandbox->setParameter('updates', $rendered_table_markdown); + + return Audit::FAIL; + } +} diff --git a/src/Audit/D8SecurityModuleUpdates.php b/src/Audit/D8SecurityModuleUpdates.php new file mode 100644 index 0000000..4713192 --- /dev/null +++ b/src/Audit/D8SecurityModuleUpdates.php @@ -0,0 +1,125 @@ +exec('drush version | grep "Drush" | sed -ne \'s/[^0-9]*\(\([0-9]\.\)\{0,4\}[0-9][^.]\).*/\1/p\'')); + + if ($drush_version === '') { + return Audit::ERROR; + } + + return $drush_version; + } + + public function isDrush8($drush_version) { + return substr($drush_version, 0, 1 ) === "8"; + } + + public function isDrush9($drush_version) { + return substr($drush_version, 0, 1 ) === "9"; + } + + /** + * @inheritdoc + */ + public function audit(Sandbox $sandbox) { + + // Detect Drush version + $drush_version = $this->getDrushVersion($sandbox); + $modules = []; + + + if ($this->isDrush8($drush_version)) { + try { + $modules = $sandbox->exec('drush pm-updatestatus --security-only --full --format=json'); + + if ($modules === '') { + $sandbox->setParameter('updates', 'No security modules to update.'); + return Audit::SUCCESS; + } + } + catch (Exception $e) { + throw new \Exception("Drush 8 command failed"); + return Audit::ERROR; + } + } + + if ($this->isDrush9($drush_version)) { + try { + $modules = $sandbox->exec('drush pm:security --format=json 2> /dev/null | cat $1'); + } + catch (Exception $e) { + throw new \Exception("Drush 9 command failed"); + return Audit::ERROR; + } + } + + if ($modules === '') { + $sandbox->setParameter('updates', 'No security modules to update.'); + return Audit::SUCCESS; + } + + $modules = json_decode($modules, TRUE); + if ($modules === null) { + return AUDIT::ERROR; + } + + if (substr($drush_version, 0, 1 ) === "8") { + $results = array_map(function($module) { + return([ + 'name' => isset($module['name']) ? $module['name'] : '', + 'existing_version' => isset($module['existing_version']) ? $module['existing_version'] : '', + 'latest_version' => isset($module['latest_version']) ? $module['latest_version'] : '', + 'recommended' => isset($module['recommended']) ? $module['recommended'] : '', + 'status_msg' => isset($module['status_msg']) ? $module['status_msg'] : '', + 'link' => isset($module['link']) ? $module['link'] : '', + ]); + }, $modules); + + $columns = ['Name', 'Current Version', 'Recommended', 'Status', 'Link']; + $rows = []; + foreach ($results as $key => $m) { + $rows[] = [ $m["name"], $m["existing_version"], $m["recommended"], $m['status_msg'], $m['link'] ]; + } + } + + if (substr($drush_version, 0, 1 ) === "9") { + $results = array_map(function($module) { + return([ + 'name' => isset($module['name']) ? $module['name'] : '', + 'version' => isset($module['version']) ? $module['version'] : '', + ]); + }, $modules); + + $columns = ['Name', 'Current Version']; + $rows = []; + foreach ($results as $key => $m) { + $rows[] = [ $m["name"], $m["version"] ]; + } + } + + $md_table = new MarkdownTableGenerator($columns, $rows); + $rendered_table_markdown = $md_table->render(); + + $sandbox->setParameter('updates', $rendered_table_markdown); + + return Audit::FAIL; + } +} diff --git a/src/Audit/FileSystemAnalysis.php b/src/Audit/FileSystemAnalysis.php index cb792ba..d69b5da 100644 --- a/src/Audit/FileSystemAnalysis.php +++ b/src/Audit/FileSystemAnalysis.php @@ -7,7 +7,6 @@ use Drutiny\Annotation\Token; use Drutiny\Audit; use Drutiny\Sandbox\Sandbox; -use Exception; /** * Filesystem analysis. @@ -64,7 +63,7 @@ public function audit(Sandbox $sandbox) { } $disk = array_map(function($line) { - $elements=preg_split('/\s+/',$line); + $elements = preg_split('/\s+/', $line); return([ 'filesystem' => isset($elements[0]) ? $elements[0] : '', @@ -74,7 +73,7 @@ public function audit(Sandbox $sandbox) { 'use%' => isset($elements[4]) ? $elements[4] : '', 'mounted' => isset($elements[5]) ? $elements[5] : '', ]); - },explode("\n",$output)); + }, explode("\n",$output)); $columns = ['Fs', 'Size', 'Used', 'Avail.', 'Use%']; $rows = []; @@ -96,8 +95,6 @@ public function audit(Sandbox $sandbox) { $sandbox->setParameter('filesystem', $rendered_table_markdown); - - if ($size < $max_size) { return Audit::SUCCESS; } diff --git a/src/Audit/SecurityModuleUpdates.php b/src/Audit/SecurityModuleUpdates.php deleted file mode 100644 index a2ce072..0000000 --- a/src/Audit/SecurityModuleUpdates.php +++ /dev/null @@ -1,74 +0,0 @@ -exec('ssh -t -o LogLevel=ERROR -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 32222 amazeelabsv4-com-prod@ssh.lagoon.amazeeio.cloud drush pm-updatestatus --format=json --full'); - - // Drush 9 site - $output = $sandbox->exec('ssh -t -o LogLevel=ERROR -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 32222 sonova-d8-prod@ssh.lagoon.amazeeio.cloud drush pm:security'); - var_dump($output); - exit(); - - // Check drush version - // If drush 9 - $output = $sandbox->drush()->pmSecurity('--format=json'); - - $issues = []; - - foreach ($modules as $name => $info) { - if (isset($info['recommended_major']) && $info['existing_major'] != $info['recommended_major']) { - $issues[] = $info; - } - elseif ($info['existing_version'] != $info['candidate_version']) { - $issues[] = $info; - } - } - - $sandbox->setParameter('updates', $issues); - - if (!count($issues)) { - return TRUE; - } - - $sec_updates = array_filter($issues, function ($info) { - return strpos($info['status_msg'], 'SECURITY') !== FALSE; - }); - - if (count($issues)) { - return Audit::FAIL; - } - - // Pure failure if all issues are security ones. - // if (count($sec_updates) === count($issues)) { - // return FALSE; - // } - // Security updates and normal updates available. - // elsif (count($sec_updates)) { - // return Audit::WARNING_FAIL; - // } - - // Just normal updates available. - // return Audit::WARNING; - } - -}