Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CMS-859] Add theme vulnerability check #103

Merged
merged 16 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ composer.lock
*.phar
vendor/*
behat/*
/.idea
40 changes: 22 additions & 18 deletions CHECKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@

All the checks in this extension should be explained in detail here. This file should be organized by command and type checker

There are currently two broad types of checkers.
* [\Pantheon\Checker](php/pantheon/checker.php): These checks simply examine a piece of data and register and alert if the data exists. For instance, does the ```wp-content/object-cache.php``` exist? If so some object caching is enabled.
There are currently two broad types of checkers.
* [\Pantheon\Checker](php/pantheon/checker.php): These checks simply examine a piece of data and register and alert if the data exists. For instance, does the ```wp-content/object-cache.php``` exist? If so some object caching is enabled.
* [\Pantheon\Filesearcher](php/pantheon/filesearcher.php): These checks are functionally the same as the above except that before being run the class uses [\Symfony\Component\Finder\Finder](http://symfony.com/doc/current/components/finder.html) to load a list of files to be checked and then runs the specified check on each file. The logic is slightly different here to allow the Finder operation to *only* run once even when multiple "Filesearcher" children are running


The Checker oject has two key methods
The Checker oject has two key methods
* ```register( Check $check )```: receives an instance of a check to run.
* ```execute()```: executes all registered checks
The checks themselves are all extensions of the [\Patheon\Checkimplementation](php/pantheon/Checkimplemtation.php) class, each containing the following methods:

The checks themselves are all extensions of the [\Patheon\Checkimplementation](php/pantheon/Checkimplemtation.php) class, each containing the following methods:
* ```init()```
* ```run()```
* ```message()```;

The Checker object holds a collection of Check objects which it iterates and invokes each of these methods. In the case of the Filesearcher object, the ```init()``` method generates the file list ( if not already present ) and the ```run()``` method is passed a $file parameter.

The message method recieves a [\Pantheon\Messsenger](php/pantheon/messenger.php) and updates the various Check object properties for output. The output of each check is simply the formatted representation of the object properties.
The message method receives a [\Pantheon\Messsenger](php/pantheon/messenger.php) and updates the various Check object properties for output. The output of each check is simply the formatted representation of the object properties.

**Check Obect Properties:**
**Check Object Properties:**
* ```$name```: machine name of the check for use at the index of the returned JSON ( if json is specified )
* ```$description```: textual description of what the check does
* ```$label```: display version of check name used on dashboard
* ```$score```: used to toggle display mechanisms in the dashboard
0: ok (green)
1: warning (orange)
2: error (red)
* ```$result```: rendered html returned for use on the dashboard ( @TODO this should eventual return raw output as well when dashboard is not the intended client )
* ```$result```: rendered html returned for use on the dashboard ( @TODO this should eventually return raw output as well when dashboard is not the intended client )
* ```$alerts```: an array of alerts to rendered for the ```$result```. Each alert should be an array: ``` array(
'code' => 2,
'class' => 'error',
Expand All @@ -47,34 +47,38 @@ This check looks for insecure code by running ````preg_match("#.*(eval|base64_de

**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
## Regular Checkers

### General
### General
**Check:** [\Pantheon\Checks\General](php/pantheon/checks/general.php)
This check does the following:
* Checks for WP_DEBUG=True, returns 'ok' if in dev, 'warning; in live
* Checks whether the debug-bar plugin is active, 'ok' in dev, 'warning' in live
* Counts active plugins. Alerts if more than 100 are active
* Checks database settings for ```home``` and ```siteurl``` and whether they match. If they do not it recommends fixing. You can do this with WP_CLI/Terminus using 'terminus wp search-replace 'domain1' 'domain2' --site=sitename --env=dev'
* Checks whether WP Super Cache and/or W3 Total Cache are found and alerts 'warning' if so.
*
*

### Database
**Database:** [\Pantheon\Checks\Database](php/pantheon/checks/database.php)
**Database:** [\Pantheon\Checks\Database](php/pantheon/checks/database.php)
This check runs the following db checks
* Runs this query ```SELECT TABLES.TABLE_NAME, TABLES.TABLE_SCHEMA, TABLES.TABLE_ROWS, TABLES.DATA_LENGTH, TABLES.ENGINE from information_schema.TABLES where TABLES.TABLE_SCHEMA = '%s'``` and checks that all tables as set to InnoDb storage engine, alerts 'error' if not and specifies a query that can be run to fix the issue.
* Also checks number of rows in the options table. If over 10,000 it alerts 'error' because this is an indication that expired transients are stacking up or that they are using a lugin that over uses the options table. A bloated options table can be a major cause of WP performance issues.
* Counts options that are set to 'autoload', alerts is more than 1,000 are found. This is relevant because WordPress runs ```SELECT * FROM wp_options WHERE autoload = 'yes'``` on every page load to prepopulate the runtime cache. In cases where the query takes to long or returns too much data this can slow down page load. The only benefit to the runtime cache comes when object caching is not in use, but it is strongly encourage that some kind of object cache is always in use.
* Also checks number of rows in the options table. If over 10,000 it alerts 'error' because this is an indication that expired transients are stacking up or that they are using a lugin that over uses the options table. A bloated options table can be a major cause of WP performance issues.
* Counts options that are set to 'autoload', alerts is more than 1,000 are found. This is relevant because WordPress runs ```SELECT * FROM wp_options WHERE autoload = 'yes'``` on every page load to prepopulate the runtime cache. In cases where the query takes to long or returns too much data this can slow down page load. The only benefit to the runtime cache comes when object caching is not in use, but it is strongly encourage that some kind of object cache is always in use.
* Looks for transients and expired transients. Some plugins will use transients regularly but not add a garbage collection cron task. Core WordPress has not garbage collection for the transient api. Over time this can cause transients to bloat the ```wp_options``` database as mentioned above.

### 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)
Checks is the ```wp-content/object-cache.php``` exists to detemine whether object caching is in use. Checks that the ```global $redis_server``` variable is not empty to determine whether redis is being used.
Checks is the ```wp-content/object-cache.php``` exists to determine whether object caching is in use. Checks that the ```global $redis_server``` variable is not empty to determine whether redis is being used.

### plugins
### Plugins
**plugins** [\Pantheon\Checks\Plugins](php/commands/checks/plugins.php)
Checks all plugins against the wpvulndb.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 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.

### 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.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

# WP Launch Check

WP Launch Check is an extension for WP-CLI designed for Pantheon.io WordPress customers. While designed initially for the Pantheon dashboard it is intended to be fully usable outside of Pantheon.
WP Launch Check is an extension for WP-CLI designed for Pantheon.io WordPress customers. While designed initially for the Pantheon dashboard it is intended to be fully usable outside of Pantheon.

[![Build Status](https://travis-ci.org/pantheon-systems/wp_launch_check.svg?branch=master)](https://travis-ci.org/pantheon-systems/wp_launch_check)
[![Actively Maintained](https://img.shields.io/badge/Pantheon-Actively_Maintained-yellow?logo=pantheon&color=FFDC28)](https://pantheon.io/docs/oss-support-levels#actively-maintained)


To use WP Launch Check simply run the ```wp launchcheck <subcommand>``` command like you would any other WP-CLI command.

For more information about WP-CLI you can visit [their github page](https://github.com/wp-cli/wp-cli).
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.

Expand All @@ -25,12 +25,13 @@ Once you've done so, you can install this package with `wp package install panth
Below is a summary of the available commands. *Full technical description of each check run by each command can be found in the [CHECKS.md](CHECKS.md)*

* **wp launchcheck cron** : Checks whether cron is enabled and what jobs are scheduled
* **wp launchcheck general**: General checks for data and best practice, i.e. are you running the debug-bar plugin or have WP_DEBUG defined? This will tell you.
* **wp launchcheck general**: General checks for data and best practice, i.e. are you running the debug-bar plugin or have WP_DEBUG defined? This will tell you.
* **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 refering 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 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



Expand Down
26 changes: 26 additions & 0 deletions features/theme.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Feature: Test WordPress for themes with known security issues

Scenario: A WordPress install with a theme with a known security issue
Given a WP install
And I run `wp theme install twentyfifteen --version=1.1 --force`
And I run `wp theme update twentyfifteen --dry-run`

When I run `wp launchcheck all --all`
Then STDOUT should contain:
"""
Found 0 themes needing updates and 1 known vulnerabilities
"""
And STDOUT should contain:
"""
Recommendation: Update themes to fix vulnerabilities
"""

Scenario: A WordPress install with no theme security issues
Given a WP install
And I run `wp theme update twentyfifteen`

When I run `wp launchcheck all --all`
Then STDOUT should contain:
"""
Found 0 themes needing updates and 0 known vulnerabilities
"""
38 changes: 30 additions & 8 deletions php/commands/launchcheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function all($args, $assoc_args) {
$config_check = new \Pantheon\Checks\Config();
$checker->register( $config_check );
$checker->execute();

if ( ! $config_check->valid_db ) {
WP_CLI::warning( 'Detected invalid database credentials, skipping remaining checks' );
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
Expand All @@ -37,7 +37,8 @@ public function all($args, $assoc_args) {
$searcher->register( new \Pantheon\Checks\Insecure() );
$searcher->register( new \Pantheon\Checks\Exploited() );
$searcher->execute();
$checker->register( new \Pantheon\Checks\Plugins(isset($assoc_args['all'])) );
$checker->register( new \Pantheon\Checks\Plugins(TRUE));
$checker->register( new \Pantheon\Checks\Themes(TRUE));
$checker->register( new \Pantheon\Checks\Cron() );
$checker->register( new \Pantheon\Checks\Objectcache() );
$checker->register( new \Pantheon\Checks\Database() );
Expand Down Expand Up @@ -132,14 +133,13 @@ function general($args, $assoc_args) {
public function object_cache($args, $assoc_args) {
$checker = new \Pantheon\Checker();
$checker->register( new \Pantheon\Checks\Objectcache() );
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
$checker->execute();
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
\Pantheon\Messenger::emit($format);
}

/**
* checks files for insecure code and checks the wpvulndb.com/api for known vulnerabilities
* Checks files for insecure code and checks the wpscan.com/api for known vulnerabilities
*
* ## OPTIONS
*
Expand All @@ -158,15 +158,14 @@ 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() );
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
$searcher->execute();
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
\Pantheon\Messenger::emit($format);
}

/**
* checks plugins for vulnerbities using the wpscan vulnerability DB
* - https://wpvulndb.com/api
* Checks plugins for vulnerabilities using the wpscan vulnerability DB
* - https://wpscan.com/api
*
* ## OPTIONS
*
Expand All @@ -184,7 +183,31 @@ public function secure($args, $assoc_args) {
public function plugins($args, $assoc_args) {
$checker = new \Pantheon\Checker();
$checker->register( new \Pantheon\Checks\Plugins( isset($assoc_args['all'])) );
$checker->execute();
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
\Pantheon\Messenger::emit($format);
}

/**
* Checks themes for vulnerabilities using the wpscan vulnerability DB
* - https://wpscan.com/api
*
* ## OPTIONS
*
* [--all]
* : check both active and inactive themes ( default is active only )
*
* [--format=<format>]
* : output as json
*
* ## EXAMPLES
*
* wp launchcheck themes --all
*
*/
public function themes($args, $assoc_args) {
$checker = new \Pantheon\Checker();
$checker->register( new \Pantheon\Checks\Themes( isset($assoc_args['all']) ) );
$checker->execute();
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
\Pantheon\Messenger::emit($format);
Expand All @@ -206,7 +229,6 @@ public function plugins($args, $assoc_args) {
public function sessions( $args, $assoc_args ) {
$searcher = new \Pantheon\Filesearcher( WP_CONTENT_DIR );
$searcher->register( new \Pantheon\Checks\Sessions() );
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
$searcher->execute();
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'raw';
\Pantheon\Messenger::emit($format);
Expand Down
4 changes: 2 additions & 2 deletions php/pantheon/checks/plugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function run() {
if ( false === $vulnerable ) {
$vulnerable = "None";
} else {
$vulnerable = sprintf('<a href="https://wpvulndb.com/plugins/%s" target="_blank" >more info</a>', $slug );
$vulnerable = sprintf('<a href="https://wpscan.com/plugins/%s" target="_blank" >more info</a>', $slug );
}

$report[$slug] = array(
Expand Down Expand Up @@ -86,7 +86,7 @@ protected function getPluginVulnerability( $plugin_slug )
}

// Set the request URL to the requested plugin
$url = 'https://wpvulndb.com/api/v3/plugins/' . $plugin_slug;
$url = 'https://wpscan.com/api/v3/plugins/' . $plugin_slug;

// Add the token to the headers
$headers = array(
Expand Down
Loading