diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df1e4f5..6654e61 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,6 @@ jobs: runs-on: ubuntu-latest env: # GITHUB_CONTEXT: ${{ toJson(github) }} - PANTHEON_WPVULNDB_API_TOKEN: ${{ secrets.PANTHEON_WPVULNDB_API_TOKEN }} WP_CLI_BIN_DIR: /tmp/wp-cli-phar DB_NAME: pantheon DB_USER: pantheon diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 140584b..b3a6486 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -12,7 +12,6 @@ jobs: env: # GITHUB_CONTEXT: ${{ toJson(github) }} - PANTHEON_WPVULNDB_API_TOKEN: ${{ secrets.PANTHEON_WPVULNDB_API_TOKEN }} WP_CLI_BIN_DIR: /tmp/wp-cli-phar DB_NAME: pantheon DB_USER: pantheon diff --git a/CHECKS.md b/CHECKS.md index 5f48f1e..04636fa 100644 --- a/CHECKS.md +++ b/CHECKS.md @@ -41,12 +41,6 @@ The message method receives a [\Pantheon\Messsenger](php/pantheon/messenger.php) **Check:** \Pantheon\Checks\Sessions; This check does a ```preg_match``` on each file passed to the run() method for the regex ```.*(session_start|SESSION).*``` -### Secure -**Check:** [\Pantheon\Check\Insecure](php/pantheon/checks/insecure.php) -This check looks for insecure code by running ````preg_match("#.*(eval|base64_decode)\(.*#:", $filecontent)```. This regex can be improved but the theory here is that ```eval``` and ```base64_decode``` are insecure because the first is discouraged even by PHP because it executes arbitrary code. The second isn't necessarily insecure by itself but is often combined with eploits to obfuscate the malicious code. ```base64_decode``` can also sometimes lead to php segfaults [ **This check is not currently used in the Pantheon dashboard ** ] - -**Check:** [\Pantheon\Check\Exploited](php/pantheon/checks/exploited.php) This check attempts to find actual exploits by running ```'.*eval\(.*base64_decode\(.*';```. The goal here is to find instance of ```eval``` operating on decoded base64, which is almost certainly a bad idea. This regex should be refined because now it technically could alert when it finds the two functions on the same page but not necessary in the right order, leading to a false positive. - ## Regular Checkers ### General @@ -68,7 +62,7 @@ This check runs the following db checks ### Cron **Cron:** [\Pantheon\Checks\Cron](php/commands/checks/cron.php) -This check simple examines whether ```DISABLE_WP_CRON``` evaluates ```true``` to see if cron has been disabled. ( We should probably also curl the ```wp-cron.php?doing_wp_cron``` and ensure we get a 200 ). Some hosts disable the default WP_Cron functionality, substituting a system cron, because the HTTP base WP_Cron can sometimes have race conditions develop causing what might be referred to as "runaway cron", in which HTTP multiple requests trigger the cron a small amount of time causing a spike in PHP/MySQL resource consumption. This check also dumps the scheduled tasks into a table using ```get_option('cron')```. +This check simple examines whether ```DISABLE_WP_CRON``` evaluates ```true``` to see if cron has been disabled. ( We should probably also curl the ```wp-cron.php?doing_wp_cron``` and ensure we get a 200 ). Some hosts disable the default WP_Cron functionality, substituting a system cron, because the HTTP base WP_Cron can sometimes have race conditions develop causing what might be referred to as "runaway cron", in which HTTP multiple requests trigger the cron a small amount of time causing a spike in PHP/MySQL resource consumption. This check also dumps the scheduled tasks into a table using ```get_option('cron')```. ### object-cache **objectcache** [\Pantheon\Checks\Cron](php/commands/checks/objectcache.php) @@ -76,8 +70,8 @@ Checks is the ```wp-content/object-cache.php``` exists to determine whether obje ### Plugins **plugins** [\Pantheon\Checks\Plugins](php/commands/checks/plugins.php) -Checks all plugins against the wpscan.com database we license. Alerts 'error' if a vulnerability is found and links to the wpvulndb.com page for more info. Also checks for available updates and alerts 'warning' if plugins needing an update are found. +Checks for available updates and alerts 'warning' if plugins needing an update are found. ### Themes **themes** [\Pantheon\Checks\Themes](php/commands/checks/themes.php) -Checks all themes against the wpscan.com database we license. Alerts 'error' if a vulnerability is found and links to the wpvulndb.com page for more info. Also checks for available updates and alerts 'warning' if themes needing an update are found. +Checks for available updates and alerts 'warning' if themes needing an update are found. diff --git a/README.md b/README.md index 482eaf3..0f786c0 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,6 @@ To use WP Launch Check simply run the ```wp launchcheck ``` command For more information about WP-CLI you can visit [their github page](https://github.com/wp-cli/wp-cli). -WP Launch Check should be considered in "BETA". Many of the checks have still not been tested in the wild. If you experience a problem please open an issue. - ## Installing Installing this package requires WP-CLI v0.23.0 or greater. Update to the latest stable release with `wp cli update`. @@ -30,9 +28,8 @@ Below is a summary of the available commands. *Full technical description of eac * **wp launchcheck database**: Checks related to the databases. * **wp launchcheck object_cache**: Checks whether object caching is enabled and if on Pantheon whether redis is enabled. * **wp launchcheck sessions**: Checks for plugins referring to the php session_start() function or the superglobal ```$SESSION``` variable. In either case, if you are on a cloud/distributed platform you will need additional configuration achieve the expected functionality - * **wp launchcheck secure**: Does some rudimentary security checks - * **wp launchcheck plugins**: Checks plugins for updates and known vulnerabilities - * **wp launchcheck themes**: Checks themes for updates and known vulnerabilities + * **wp launchcheck plugins**: Checks plugins for updates + * **wp launchcheck themes**: Checks themes for updates diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 26aab1f..85f3811 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -77,9 +77,6 @@ private static function get_process_env_variables() { if ( $config_path = getenv( 'WP_CLI_CONFIG_PATH' ) ) { $env['WP_CLI_CONFIG_PATH'] = $config_path; } - if ( $wpvulndb_api_token = getenv( 'PANTHEON_WPVULNDB_API_TOKEN' ) ) { - $env['PANTHEON_WPVULNDB_API_TOKEN'] = $wpvulndb_api_token; - } return $env; } diff --git a/features/exploited.feature b/features/exploited.feature deleted file mode 100644 index cb29fd1..0000000 --- a/features/exploited.feature +++ /dev/null @@ -1,76 +0,0 @@ -Feature: Test WordPress for exploited files - - Scenario: A WordPress install with an exploited file - Given a WP install - And a wp-content/mu-plugins/exploited.php file: - """ - register( new \Pantheon\Checks\Sessions() ); - $searcher->register( new \Pantheon\Checks\Insecure() ); - $searcher->register( new \Pantheon\Checks\Exploited() ); $searcher->execute(); $checker->register( new \Pantheon\Checks\Plugins(TRUE)); $checker->register( new \Pantheon\Checks\Themes(TRUE)); @@ -158,33 +156,7 @@ public function object_cache($args, $assoc_args) { } /** - * Checks files for insecure code and checks the wpscan.com/api for known vulnerabilities - * - * ## OPTIONS - * - * [--skip=] - * : a regular expression matching directories to skip - * - * [--format=] - * : output as json - * - * ## EXAMPLES - * - * wp launchcheck secure --skip=wp-content/themes - * - */ - public function secure($args, $assoc_args) { - $searcher = new \Pantheon\Filesearcher( WP_CONTENT_DIR ); - $searcher->register( new \Pantheon\Checks\Insecure() ); - $searcher->register( new \Pantheon\Checks\Exploited() ); - $searcher->execute(); - $format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw'; - \Pantheon\Messenger::emit($format); - } - - /** - * Checks plugins for vulnerabilities using the wpscan vulnerability DB - * - https://wpscan.com/api + * Checks plugins for available updates * * ## OPTIONS * @@ -208,8 +180,7 @@ public function plugins($args, $assoc_args) { } /** - * Checks themes for vulnerabilities using the wpscan vulnerability DB - * - https://wpscan.com/api + * Checks themes for available updates * * ## OPTIONS * diff --git a/php/pantheon/checks/exploited.php b/php/pantheon/checks/exploited.php deleted file mode 100644 index 1208bc2..0000000 --- a/php/pantheon/checks/exploited.php +++ /dev/null @@ -1,51 +0,0 @@ -name = 'exploited'; - $this->action = 'No exploits found.'; - $this->description = 'Looking for exploited files.'; - $this->score = 0; - $this->result = ''; - $this->label = 'Probable exploits'; - $this->key = 'exploits'; - $this->alerts = array(); - self::$instance = $this; - return $this; - } - - public function run($file) { - $regex = 'eval\(.*base64_decode\(.*'; - $file_contents = $file->getContents(); - preg_match('#'.$regex.'#', $file_contents, $matches, PREG_OFFSET_CAPTURE ); - if ( $matches ) { - foreach ($matches as $match) { - $linenum = substr_count(substr($file_contents, 0, $match[1]), "\n") + 1; - $this->alerts[] = array('class'=> 'warning', 'data'=> array($file->getRelativePathname(), $linenum, substr($match[0],0,50))); - } - } - return $this; - } - - public function message(Messenger $messenger) { - if (!empty($this->alerts)) { - $details = sprintf( "Found %s files that contain likely exploits \n\t-> %s", - count($this->alerts), - View::make('table',array('headers'=>array('File','Line','Match'),'rows'=> $this->alerts)) - ); - $this->score = 2; - $this->result .= $details; - $this->action = "You should deactivate this plugin unless you can verify this is the intended use."; - } else { - $this->result .= "No exploits found."; - } - $messenger->addMessage(get_object_vars($this)); - } -} diff --git a/php/pantheon/checks/insecure.php b/php/pantheon/checks/insecure.php deleted file mode 100644 index a70d4b7..0000000 --- a/php/pantheon/checks/insecure.php +++ /dev/null @@ -1,52 +0,0 @@ -name = 'insecure'; - $this->action = 'We did not find any files running risky functions.'; - $this->description = 'PHP files running eval or base64_decode on user input can be insecure.'; - $this->score = 0; - $this->result = ''; - $this->label = 'Risky PHP Functions'; - return $this; - } - - public function run($file) { - $regex = '(eval|base64_decode)\(.*'; - $file_contents = $file->getContents(); - preg_match('#'.$regex.'#', $file_contents, $matches, PREG_OFFSET_CAPTURE ); - if ( $matches ) { - $note = ''; - foreach($matches as $match) { - // Don't flag if the file in question is inside wp-redis. - if ( false !== strpos( $file->getPath(), 'wp-redis' ) ) { - continue; - } - $linenum = substr_count(substr($file_contents, 0, $match[1]), "\n") + 1; - $this->alerts[] = array( 'class'=>'warning', 'data'=> array( $file->getRelativePathname(), $linenum, substr($match[0],0,50))); - } - } - return $this; - } - - public function message(Messenger $messenger) { - if (!empty($this->alerts)) { - $details = sprintf( "Found %s files that reference risky function. \n\t-> %s", - count($this->alerts), - View::make('table', array( 'headers'=>array('File','Line','Match'),'rows'=>$this->alerts ) ) - ); - $this->score = 1; - $this->result .= $details; - $this->action = "You do not need to deactivate these files, but please scrutinize them in the event of a security issue."; - } - $messenger->addMessage(get_object_vars($this)); - return $this; - } -} diff --git a/php/pantheon/checks/namespace.php b/php/pantheon/checks/namespace.php deleted file mode 100644 index b697ac6..0000000 --- a/php/pantheon/checks/namespace.php +++ /dev/null @@ -1,36 +0,0 @@ -check_all_plugins = $check_all_plugins; } @@ -39,8 +37,6 @@ public function run() { $all_plugins = Utils::sanitize_data( get_plugins() ); $update = Utils::sanitize_data( get_plugin_updates() ); $report = array(); - $should_check_vulnerabilities = Common\get_wp_vuln_api_token(); - $vulnerable = false; foreach( $all_plugins as $plugin_path => $data ) { $slug = $plugin_path; @@ -61,182 +57,50 @@ public function run() { 'available' => (string) $available, 'needs_update' => (string) $needs_update, ); - - // If we're checking for vulnerabilities, do stuff. - if ( $should_check_vulnerabilities ) { - $vulnerable = $this->is_vulnerable($slug, $data['Version']); - - if ( $vulnerable ) { - // Todo: Replace this URL with a Patchstack URL - $vulnerable = sprintf('more info', $slug ); - } else { - $vulnerable = "None"; - } - - $report[ $slug ]['vulnerable'] = $vulnerable; - } } $this->alerts = $report; } - /** - * Checks the plugin slug against the vulnerability db - * @param $plugin_slug string (required) string representing the plugin slug - * @return array containing vulnerability info or false - * @todo Refactor to use Patchstack API. - */ - protected function getPluginVulnerability( $plugin_slug ) - { - // Get the vulnerability API token from the platform - $wpvulndb_api_token = Common\get_wp_vuln_api_token(); - - // Fail silently if there is no API token. - if( false === $wpvulndb_api_token || empty( $wpvulndb_api_token ) ) { - return false; + public function message(Messenger $messenger) { + if (empty($this->alerts)) { + // Nothing to do. Return early. + $this->result .= __( 'No plugins found' ); + $messenger->addMessage(get_object_vars($this)); + return; } - // Set the request URL to the requested plugin - $url = 'https://wpscan.com/api/v3/plugins/' . $plugin_slug; - - // Add the token to the headers $headers = array( - 'Content-Type: application/json', - 'User-Agent: pantheon/wp_launch_check', - 'Authorization: Token token=' . $wpvulndb_api_token + 'slug'=> __( 'Plugin' ), + 'installed'=> __( 'Current' ), + 'available' => __( 'Available' ), + 'needs_update'=> __( 'Needs Update' ), ); - // Make the request to the API - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 5); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $result = curl_exec($ch); - curl_close($ch); - - // Return false if no result from the API - if( false === $result ) { - return false; - } + $rows = array(); + $count_update = 0; - // Decode the result from the API - $result = json_decode( $result, true ); - - // Return false if the specified plugin slug is not in the result - if( ! isset( $result[$plugin_slug] ) ) { - return false; - } - - // Return the requested plugin vulnerability info - return $result[$plugin_slug]; - } - - /** - * Checks a plugin by slug and version for vulnerabilities - * @param $plugin_slug string (required) string representing the plugin slug - * @param $current_version string (required) string representing the plugin version - * - * @return array containing the vulnerability or false - */ - public function is_vulnerable($plugin_slug, $current_version) { - - // Fetch the plugin data if we don't have it already - if( !isset( $plugin_data[$plugin_slug] ) ){ - $plugin_results = $this->getPluginVulnerability( $plugin_slug ); - - // Return false if no plugin results from the vulnerability API - if( false === $plugin_results ){ - return false; + foreach( $this->alerts as $alert ) { + $class = 'ok'; + if ($alert['needs_update']) { + $class = 'warning'; + $count_update++; } + $rows[] = array('class'=>$class, 'data' => $alert); } - // No issues if the plugin has no vulnerabilities - if ( ! isset( $plugin_results['vulnerabilities'] ) || empty( $plugin_results['vulnerabilities'] ) ) { - return false; - } - - - // Loop through all vulnerabilities - foreach ( $plugin_results['vulnerabilities'] as $vulnerability ) { - - // If the vulnerability hasn't been fixed, then there's an issue - if ( ! isset( $vulnerability['fixed_in'] ) ) { - return $vulnerability; - } - - // If the vulnerability has been fixed, but not in the current version, there's an issue - if ( version_compare( $vulnerability['fixed_in'], $current_version,'>' ) ){ - return $vulnerability; - } + $updates_message = $count_update === 1 ? __( 'Found one plugin needing updates' ) : sprintf( _n( 'Found %d plugin needing updates', 'Found %d plugins needing updates', $count_update ), $count_update ); + $result_message = $updates_message . ' ...'; + $rendered = PHP_EOL; + $rendered .= "$result_message \n" . PHP_EOL; + $rendered .= View::make('table', array('headers'=>$headers,'rows'=>$rows)); + $this->result .= $rendered; + if ($count_update > 0) { + $this->score = 1; + $this->action = __( 'You should update all out-of-date plugins' );; } - // If we get this far the current version has no vulnerabilities - return false; - } - - public function message(Messenger $messenger) { - $plugin_message = __( 'You should update all out-of-date plugins' ); - $vuln_message = __( 'Update plugins to fix vulnerabilities' ); - $no_plugins_message = __( 'No plugins found' ); - $should_check_vulnerabilities = Common\get_wp_vuln_api_token(); - - if (!empty($this->alerts)) { - $headers = array( - 'slug'=> __( 'Plugin' ), - 'installed'=> __( 'Current' ), - 'available' => __( 'Available' ), - 'needs_update'=> __( 'Needs Update' ), - ); - - if ( $should_check_vulnerabilities ) { - $headers['vulnerable'] = __( ' Vulnerabilities' ); - } - - $rows = array(); - $count_update = 0; - $count_vuln = 0; - - foreach( $this->alerts as $alert ) { - $class = 'ok'; - if ($alert['needs_update']) { - $class = 'warning'; - $count_update++; - } - - if ( $should_check_vulnerabilities && 'None' !== $alert['vulnerable']) { - $class = 'error'; - $count_vuln++; - } - - $rows[] = array('class'=>$class, 'data' => $alert); - } - - $updates_message = $count_update === 1 ? __( 'Found one plugin needing updates' ) : sprintf( _n( 'Found %d plugin needing updates', 'Found %d plugins needing updates', $count_update ), $count_update ); - $result_message = ! $should_check_vulnerabilities ? - // Not checking vulnerabilities message. - $updates_message . ' ...': - // Checking vulnerabilities message. - $updates_message . ' ' . - ( $count_vuln === 1 ? __( 'Also found one plugin with known vulnerabilities ...' ) : sprintf( _n( 'Also found %d plugin with known vulnerabilities ...', 'Also found %d plugins with known vulnerabilities ...', $count_vuln ), $count_vuln ) ); - $rendered = PHP_EOL; - $rendered .= "$result_message \n" . PHP_EOL; - $rendered .= View::make('table', array('headers'=>$headers,'rows'=>$rows)); - - $this->result .= $rendered; - if ($count_update > 0) { - $this->score = 1; - $this->action = $plugin_message; - } - - if ($count_vuln > 0) { - $this->score = 2; - $this->action = $vuln_message; - } - } else { - $this->result .= $no_plugins_message; - } $messenger->addMessage(get_object_vars($this)); } } diff --git a/php/pantheon/checks/themes.php b/php/pantheon/checks/themes.php index 69805c3..7381236 100644 --- a/php/pantheon/checks/themes.php +++ b/php/pantheon/checks/themes.php @@ -12,8 +12,6 @@ class Themes extends Checkimplementation { public $alerts = array(); public function __construct($check_all_themes) { - require_once __DIR__ . '/namespace.php'; - $this->check_all_themes = $check_all_themes; } @@ -40,8 +38,6 @@ public function run() { $all_themes = Utils::sanitize_data( wp_get_themes() ); $update = Utils::sanitize_data( get_theme_updates() ); $report = array(); - $should_check_vulnerabilities = Common\get_wp_vuln_api_token(); - $vulnerable = false; foreach( $all_themes as $theme_path => $data ) { $slug = $theme_path; @@ -49,7 +45,7 @@ public function run() { $slug = substr($theme_path, 0, stripos($theme_path,'/')); } - // Check if we only want to scan the active theme. + // Check if we only want to check the active theme. if (!$this->check_all_themes) { // If theme list index doesn't match current theme, skip. if ($current_theme->stylesheet !== $slug) { @@ -73,177 +69,49 @@ public function run() { 'available' => (string) $available, 'needs_update' => (string) $needs_update, ); - - // If we're checking for vulnerabilities, do stuff. - if ( $should_check_vulnerabilities ) { - $vulnerable = $this->is_vulnerable($slug, $version); - - if ( $vulnerable ) { - // Todo: Replace this link with one to Patchstack. - $vulnerable = sprintf('more info', $slug ); - } else { - $vulnerable = "None"; - } - - $report[ $slug ]['vulnerable'] = $vulnerable; - } } $this->alerts = $report; } - /** - * Checks the theme slug against the vulnerability db - * @param $theme_slug string (required) string representing the theme slug - * - * @return array containing vulnerability info or false - * @throws \Exception - * @todo Refactor this to use the Patchstack API - */ - protected function getThemeVulnerability($theme_slug ) { - $wpvulndb_api_token = Common\get_wp_vuln_api_token(); - - // Fail silently if there is no API token. - if( false === $wpvulndb_api_token || empty( $wpvulndb_api_token ) ) { - return false; + public function message(Messenger $messenger) { + if (empty($this->alerts)) { + // Nothing to do. Return early. + $this->result .= __( 'No themes found' ); + $messenger->addMessage(get_object_vars($this)); + return; } - // Set the request URL to the requested theme - $url = 'https://wpscan.com/api/v3/themes/' . $theme_slug; - - // Add the token to the headers + $theme_message = __( 'You should update all out-of-date themes' ); $headers = array( - 'Content-Type: application/json', - 'User-Agent: pantheon/wp_launch_check', - 'Authorization: Token token=' . $wpvulndb_api_token + 'slug' => __( 'Theme' ), + 'installed' => __( 'Current' ), + 'available' => __( 'Available' ), + 'needs_update' => __( 'Needs Update' ), ); - // Make the request to the API - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 5); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $result = curl_exec($ch); - curl_close($ch); - - // Return false if no result from the API - if( false === $result ) { - return false; - } - - // Decode the result from the API - $result = json_decode( $result, true ); - - // Return false if the specified theme slug is not in the result - if( ! isset( $result[$theme_slug] ) ) { - return false; - } - - // Return the requested theme vulnerability info - return $result[$theme_slug]; - } - - /** - * Checks a theme by slug and version for vulnerabilities - * @param $theme_slug string (required) string representing the theme slug - * @param $current_version string (required) string representing the theme version - * - * @return array containing the vulnerability or false - * @throws \Exception - */ - public function is_vulnerable($theme_slug, $current_version) { - - // Fetch the theme data if we don't have it already - if( !isset( $theme_data[$theme_slug] ) ){ - $theme_results = $this->getThemeVulnerability( $theme_slug ); - - // Return false if no theme results from the vulnerability API - if( false === $theme_results ){ - return false; + $rows = array(); + $count_update = 0; + foreach( $this->alerts as $alert ) { + $class = 'ok'; + if ($alert['needs_update']) { + $class = 'warning'; + $count_update++; } + $rows[] = array('class'=>$class, 'data' => $alert); } - // No issues if the theme has no vulnerabilities - if ( empty( $theme_results['vulnerabilities'] ) ) { - return false; - } - - - // Loop through all vulnerabilities - foreach ( $theme_results['vulnerabilities'] as $vulnerability ) { - - // If the vulnerability hasn't been fixed, then there's an issue - if ( ! isset( $vulnerability['fixed_in'] ) ) { - return $vulnerability; - } - - // If the vulnerability has been fixed, but not in the current version, there's an issue - if ( version_compare( $vulnerability['fixed_in'], $current_version,'>' ) ){ - return $vulnerability; - } + $updates_message = $count_update === 1 ? __( 'Found one theme needing updates' ) : sprintf( _n( 'Found %d theme needing updates', 'Found %d themes needing updates', $count_update ), $count_update ); + $result_message = $updates_message . ' ...'; + $rendered = PHP_EOL; + $rendered .= "$result_message \n" .PHP_EOL; + $rendered .= View::make('table', array('headers'=>$headers,'rows'=>$rows)); + $this->result .= $rendered; + if ($count_update > 0) { + $this->score = 1; + $this->action = $theme_message; } - // If we get this far the current version has no vulnerabilities - return false; - } - - public function message(Messenger $messenger) { - if (!empty($this->alerts)) { - $should_check_vulnerabilities = Common\get_wp_vuln_api_token(); - $theme_message = __( 'You should update all out-of-date themes' ); - $vuln_message = __( 'Update themes to fix vulnerabilities' ); - $no_themes_message = __( 'No themes found' ); - $headers = array( - 'slug' => __( 'Theme' ), - 'installed' => __( 'Current' ), - 'available' => __( 'Available' ), - 'needs_update' => __( 'Needs Update' ), - ); - if ( $should_check_vulnerabilities ) { - $headers['vulnerable'] = __( 'Vulnerable' ); - } - - $rows = array(); - $count_update = 0; - $count_vuln = 0; - foreach( $this->alerts as $alert ) { - $class = 'ok'; - if ($alert['needs_update']) { - $class = 'warning'; - $count_update++; - } - if ( $should_check_vulnerabilities && 'None' !== $alert['vulnerable']) { - $class = 'error'; - $count_vuln++; - } - $rows[] = array('class'=>$class, 'data' => $alert); - } - - $updates_message = $count_update === 1 ? __( 'Found one theme needing updates' ) : sprintf( _n( 'Found %d theme needing updates', 'Found %d themes needing updates', $count_update ), $count_update ); - $result_message = ! $should_check_vulnerabilities ? - // Not checking vulnerabilities message. - $updates_message . ' ...': - // Checking vulnerabilities message. - $updates_message . ' ' . - ( $count_vuln === 1 ? __( 'Also found one theme with known vulnerabilities ...' ) : sprintf( _n( 'Also found %d theme with known vulnerabilities ...', 'Also found %d themes with known vulnerabilities ...', $count_vuln ), $count_vuln ) ); - $rendered = PHP_EOL; - $rendered .= "$result_message \n" .PHP_EOL; - $rendered .= View::make('table', array('headers'=>$headers,'rows'=>$rows)); - - $this->result .= $rendered; - if ($count_update > 0) { - $this->score = 1; - $this->action = $theme_message; - } - - if ( $should_check_vulnerabilities && $count_vuln > 0 ) { - $this->score = 2; - $this->action = $vuln_message; - } - } else { - $this->result .= $no_themes_message; - } $messenger->addMessage(get_object_vars($this)); } }