installed plugin WPScan
version 1.15.1
This commit is contained in:
160
wp-content/plugins/wpscan/app/Account.php
Normal file
160
wp-content/plugins/wpscan/app/Account.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Account.
|
||||
*
|
||||
* Deals with user's wpvulndb API user accounts.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Account {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param object $parent parent.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
|
||||
add_action( 'admin_init', array( $this, 'add_account_summary_meta_box' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update account status by calling the /status endpoint.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $api_token
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function update_account_status( $api_token = null ) {
|
||||
$current = get_option( $this->parent->OPT_ACCOUNT_STATUS, array() );
|
||||
$updated = $current;
|
||||
|
||||
$req = $this->parent->api_get( '/status', $api_token );
|
||||
|
||||
if ( is_object( $req ) ) {
|
||||
$updated['plan'] = $req->plan;
|
||||
|
||||
// Enterprise users.
|
||||
if ( -1 === $req->requests_remaining ) {
|
||||
$updated['limit'] = __( 'unlimited', 'wpscan' );
|
||||
$updated['remaining'] = __( 'unlimited', 'wpscan' );
|
||||
$updated['reset'] = __( 'unlimited', 'wpscan' );
|
||||
} else {
|
||||
$updated['limit'] = $req->requests_limit;
|
||||
$updated['remaining'] = $req->requests_remaining;
|
||||
$updated['reset'] = $req->requests_reset;
|
||||
}
|
||||
|
||||
update_option( $this->parent->OPT_ACCOUNT_STATUS, $updated );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function add_account_summary_meta_box() {
|
||||
if ( $this->parent->classes['settings']->api_token_set() ) {
|
||||
add_meta_box(
|
||||
'wpscan-account-summary',
|
||||
__( 'Account Status', 'wpscan' ),
|
||||
array( $this, 'do_meta_box_account_summary' ),
|
||||
'wpscan',
|
||||
'side',
|
||||
'low'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account status
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function get_account_status() {
|
||||
$defaults = array(
|
||||
'plan' => 'None',
|
||||
'limit' => 25,
|
||||
'remaining' => 25,
|
||||
'reset' => time(),
|
||||
);
|
||||
|
||||
return get_option( $this->parent->OPT_ACCOUNT_STATUS, $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render account status metabox
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function do_meta_box_account_summary() {
|
||||
extract( $this->get_account_status() );
|
||||
|
||||
if ( 'enterprise' !== $plan ) {
|
||||
if ( ! isset( $limit ) || ! is_numeric( $limit ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset time in hours.
|
||||
$diff = $reset - time();
|
||||
$days = floor( $diff / ( 60 * 60 * 24 ) );
|
||||
$hours = round( ( $diff - $days * 60 * 60 * 24 ) / ( 60 * 60 ) );
|
||||
$hours_display = $hours > 1 ? __( 'Hours', 'wpscan' ) : __( 'Hour', 'wpscan' );
|
||||
|
||||
// Used.
|
||||
$used = $limit - $remaining;
|
||||
|
||||
// Usage percentage.
|
||||
$percentage = 0 !== $limit ? ( $used * 100 ) / $limit : 0;
|
||||
|
||||
// Usage color.
|
||||
if ( $percentage < 50 ) {
|
||||
$usage_color = 'wpscan-status-green';
|
||||
} elseif ( $percentage >= 50 && $percentage < 95 ) {
|
||||
$usage_color = 'wpscan-status-orange';
|
||||
} else {
|
||||
$usage_color = 'wpscan-status-red';
|
||||
}
|
||||
} else {
|
||||
// For enterprise users.
|
||||
$used = $limit;
|
||||
$hours = $reset;
|
||||
$hours_display = null;
|
||||
$usage_color = 'wpscan-status-green';
|
||||
}
|
||||
|
||||
// Upgrade button.
|
||||
$btn_text = 'free' === $plan ? __( 'Upgrade', 'wpscan' ) : __( 'Manage', 'wpscan' );
|
||||
$btn_url = WPSCAN_PROFILE_URL;
|
||||
|
||||
// Output data.
|
||||
echo '<ul>';
|
||||
echo '<li>' . __( 'Plan', 'wpscan' ) . '<span>' . esc_html( $plan ) . '</span></li>';
|
||||
|
||||
if ( 'enterprise' !== $plan ) {
|
||||
echo '<li>' . __( 'Usage', 'wpscan' ) . "<span class='$usage_color'> $used / $limit </span></li>";
|
||||
echo '<li>' . __( 'Resets In', 'wpscan' ) . "<span> $hours $hours_display </span></li>";
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
// Output upgrade/manage button.
|
||||
echo "<a class='button button-primary' href='$btn_url' target='_blank'>$btn_text</a>";
|
||||
}
|
||||
}
|
244
wp-content/plugins/wpscan/app/Checks/Check.php
Normal file
244
wp-content/plugins/wpscan/app/Checks/Check.php
Normal file
@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan\Checks;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Security Checks.
|
||||
*
|
||||
* Handles the security checks system.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class Check {
|
||||
/**
|
||||
* A list of identified vulnerabilities for this check.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @var array|null
|
||||
*/
|
||||
public $vulnerabilities;
|
||||
|
||||
/**
|
||||
* Actions list.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @var array|null
|
||||
*/
|
||||
public $actions = array();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $id id.
|
||||
* @param string $dir dir.
|
||||
* @param string $parent parent.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public final function __construct( $id, $dir, $parent ) {
|
||||
$this->id = $id;
|
||||
$this->dir = $dir;
|
||||
$this->parent = $parent;
|
||||
|
||||
$count = $this->get_vulnerabilities_count();
|
||||
|
||||
$this->actions[] = array(
|
||||
'id' => 'run',
|
||||
'title' => __( 'Run', 'wpscan' ),
|
||||
'method' => 'run',
|
||||
);
|
||||
|
||||
if ( $count > 0 ) {
|
||||
$this->actions[] = array(
|
||||
'id' => 'dismiss',
|
||||
'title' => __( 'Dismiss', 'wpscan' ),
|
||||
'method' => 'dismiss',
|
||||
'confirm' => true,
|
||||
);
|
||||
}
|
||||
|
||||
if ( method_exists( $this, 'init' ) ) {
|
||||
$this->init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check title.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
abstract public function title();
|
||||
|
||||
/**
|
||||
* Check description.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
abstract public function description();
|
||||
|
||||
/**
|
||||
* Success message.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
abstract public function success_message();
|
||||
|
||||
/**
|
||||
* Add vulnerability
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $title The vulnerability title.
|
||||
* @param string $severity The severity, can be critical, high, medium, low and info.
|
||||
* @param string $id Unique string to represent the vulnerability in the report object.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
final public function add_vulnerability( $title, $severity, $id, $remediation_url ) {
|
||||
$vulnerability = array(
|
||||
'title' => $title,
|
||||
'severity' => $severity,
|
||||
'id' => $id,
|
||||
'remediation_url' => $remediation_url,
|
||||
);
|
||||
|
||||
$this->vulnerabilities[] = $vulnerability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vulnerabilities.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array|null
|
||||
*/
|
||||
final public function get_vulnerabilities() {
|
||||
if ( ! empty( $this->vulnerabilities ) ) {
|
||||
return $this->vulnerabilities;
|
||||
}
|
||||
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
if ( isset( $report['security-checks'] ) ) {
|
||||
if ( isset( $report['security-checks'][ $this->id ] ) ) {
|
||||
return $report['security-checks'][ $this->id ]['vulnerabilities'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item non-ignored vulnerabilities count
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function get_vulnerabilities_count() {
|
||||
$vulnerabilities = $this->get_vulnerabilities();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
if ( empty( $vulnerabilities ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach ( $vulnerabilities as $key => &$item ) {
|
||||
if ( in_array( $item['id'], $ignored, true ) ) {
|
||||
unset( $vulnerabilities[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return count( $vulnerabilities );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss action
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function dismiss() {
|
||||
$report = $this->parent->get_report();
|
||||
$updated = $report;
|
||||
|
||||
if ( isset( $updated['security-checks'] ) ) {
|
||||
if ( isset( $updated['security-checks'][ $this->id ] ) ) {
|
||||
$updated['security-checks'][ $this->id ]['vulnerabilities'] = array();
|
||||
}
|
||||
}
|
||||
|
||||
if ( $report === $updated ) {
|
||||
return true;
|
||||
} else {
|
||||
return update_option( $this->parent->OPT_REPORT, $updated );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run action.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function run() {
|
||||
$report = $this->parent->get_report();
|
||||
$updated = $report;
|
||||
|
||||
if ( empty( $updated ) ) {
|
||||
$updated = array(
|
||||
'security-checks' => array(),
|
||||
'plugins' => array(),
|
||||
'themes' => array(),
|
||||
'wordpress' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $updated['security-checks'][ $this->id ] ) ) {
|
||||
$updated['security-checks'][ $this->id ] = array();
|
||||
}
|
||||
|
||||
$this->perform();
|
||||
|
||||
if ( is_array( $this->vulnerabilities ) ) {
|
||||
$updated['security-checks'][ $this->id ]['vulnerabilities'] = $this->vulnerabilities;
|
||||
|
||||
$this->parent->maybe_fire_issue_found_action('security-check', $this->id, $updated['security-checks'][ $this->id ]);
|
||||
} else {
|
||||
$updated['security-checks'][ $this->id ]['vulnerabilities'] = array();
|
||||
}
|
||||
|
||||
if ( $report === $updated ) {
|
||||
return true;
|
||||
} else {
|
||||
return update_option( $this->parent->OPT_REPORT, $updated );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the check and save the results.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
abstract public function perform();
|
||||
}
|
415
wp-content/plugins/wpscan/app/Checks/System.php
Normal file
415
wp-content/plugins/wpscan/app/Checks/System.php
Normal file
@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan\Checks;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* System.
|
||||
*
|
||||
* General system functions.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class System {
|
||||
|
||||
public $OPT_FATAL_ERRORS = 'wpscan_fatal_errors';
|
||||
|
||||
// Schedule.
|
||||
public $WPSCAN_SECURITY_SCHEDULE = 'wpscan_security_schedule';
|
||||
|
||||
// Events Inline.
|
||||
public $OPT_EVENTS_INLINE = 'wpscan_events_inline';
|
||||
|
||||
// Current running events.
|
||||
public $current_running = '';
|
||||
/**
|
||||
* A list of registered checks.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @var array
|
||||
*/
|
||||
public $checks = array();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param object $parent parent.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
$this->current_running = get_option( $this->OPT_EVENTS_INLINE );
|
||||
|
||||
register_shutdown_function( array( $this, 'catch_errors' ) );
|
||||
|
||||
add_action( 'admin_notices', array( $this, 'display_errors' ) );
|
||||
|
||||
add_action( 'plugins_loaded', array( $this, 'load_checks' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
|
||||
add_action( 'wp_ajax_wpscan_check_action', array( $this, 'handle_actions' ) );
|
||||
|
||||
add_action( $this->WPSCAN_SECURITY_SCHEDULE, array( $this, 'security_check_now' ), 99 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Admin Scripts
|
||||
*
|
||||
* @param string $hook parent.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function admin_enqueue( $hook ) {
|
||||
if ( $hook === $this->parent->page_hook ) {
|
||||
wp_enqueue_script(
|
||||
'wpscan-security-checks',
|
||||
plugins_url( 'assets/js/security-checks.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'jquery-ui-tooltip' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load checks files.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function load_checks() {
|
||||
$dir = $this->parent->plugin_dir . 'security-checks';
|
||||
$folders = array_diff( scandir( $dir ), array( '..', '.' ) );
|
||||
|
||||
foreach ( $folders as $folder ) {
|
||||
$file = "$dir/$folder/check.php";
|
||||
|
||||
if ( '.' === $folder[0] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
require_once $file;
|
||||
|
||||
$data = get_file_data( $file, array( 'classname' => 'classname' ) );
|
||||
|
||||
$data['instance'] = new $data['classname']( $folder, "$dir/$folder", $this->parent );
|
||||
|
||||
$this->checks[ $folder ] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a shutdown hook to catch errors
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function catch_errors() {
|
||||
$error = error_get_last();
|
||||
|
||||
if ( $error && $error['type'] ) {
|
||||
|
||||
if ( basename( $error['file'] ) == 'check.php' ) {
|
||||
$errors = get_option( $this->OPT_FATAL_ERRORS, array() );
|
||||
|
||||
array_push( $errors, $error );
|
||||
|
||||
update_option( $this->OPT_FATAL_ERRORS, array_unique( $errors ) );
|
||||
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
$report['cache'] = strtotime( current_time( 'mysql' ) );
|
||||
|
||||
update_option( $this->parent->OPT_REPORT, $report );
|
||||
|
||||
$this->parent->classes['account']->update_account_status();
|
||||
|
||||
delete_transient( $this->parent->WPSCAN_TRANSIENT_CRON );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display fatal errors
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function display_errors() {
|
||||
$screen = get_current_screen();
|
||||
$errors = get_option( $this->OPT_FATAL_ERRORS, array() );
|
||||
|
||||
if ( strstr( $screen->id, $this->parent->classes['report']->page ) ) {
|
||||
foreach ( $errors as $err ) {
|
||||
$msg = explode( 'Stack', $err['message'] )[0];
|
||||
$msg = trim( $msg );
|
||||
|
||||
echo "<div class='notice notice-error'><p>$msg</p></div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List vulnerabilities in the report.
|
||||
*
|
||||
* @param object $check - The check instance.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function list_check_vulnerabilities( $instance ) {
|
||||
$vulnerabilities = $instance->get_vulnerabilities();
|
||||
$count = $instance->get_vulnerabilities_count();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
$not_checked_text = __( 'Not checked yet. Click the Run button to run a scan', 'wpscan' );
|
||||
|
||||
if ( ! isset( $vulnerabilities ) ) {
|
||||
echo esc_html( $not_checked_text );
|
||||
} elseif ( empty( $vulnerabilities ) || 0 === $count ) {
|
||||
echo esc_html( $instance->success_message() );
|
||||
} else {
|
||||
$list = array();
|
||||
|
||||
foreach ( $vulnerabilities as $item ) {
|
||||
if ( in_array( $item['id'], $ignored, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$html = "<div class='vulnerability'>";
|
||||
$html .= "<span class='vulnerability-severity'>";
|
||||
$html .= "<span class='wpscan-" . esc_attr( $item['severity'] ) . "'>" . esc_html( $item['severity'] ) ."</span>";
|
||||
$html .= '</span>';
|
||||
$html .= "<div class='vulnerability-title'>" . wp_kses( $item['title'], array( 'a' => array( 'href' => array() ) ) ) . '</div>';
|
||||
$html .= "<div class='vulnerability-remediation'> <a href='" . $item['remediation_url'] . "' target='_blank'>Click here for further info</a></div>";
|
||||
$html .= '</div>';
|
||||
$list[] = $html;
|
||||
}
|
||||
|
||||
echo join( '<br>', $list );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return vulnerabilities in the report.
|
||||
*
|
||||
* @param object $check - The check instance.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.14.4
|
||||
*
|
||||
*/
|
||||
public function get_check_vulnerabilities( $instance ) {
|
||||
$vulnerabilities = $instance->get_vulnerabilities();
|
||||
$count = $instance->get_vulnerabilities_count();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
$not_checked_text = __( 'Not checked yet. Click the Run button to run a scan', 'wpscan' );
|
||||
|
||||
if ( ! isset( $vulnerabilities ) ) {
|
||||
return esc_html( $not_checked_text );
|
||||
} elseif ( empty( $vulnerabilities ) || 0 === $count ) {
|
||||
return esc_html( $instance->success_message() );
|
||||
} else {
|
||||
$list = array();
|
||||
|
||||
foreach ( $vulnerabilities as $item ) {
|
||||
if ( in_array( $item['id'], $ignored, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$html = "<div class='vulnerability'>";
|
||||
$html .= "<div class='vulnerability-severity'>";
|
||||
$html .= "<span class='wpscan-" . esc_attr( $item['severity'] ) . "'>" . esc_html( $item['severity'] ) . '</span>';
|
||||
$html .= '</div>';
|
||||
$html .= "<div class='vulnerability-title'>" . wp_kses( $item['title'], array( 'a' => array( 'href' => array() ) ) ) . '</div>';
|
||||
$html .= '</div>';
|
||||
$list[] = $html;
|
||||
}
|
||||
|
||||
return join( '<br>', $list );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display actions buttons
|
||||
*
|
||||
* @param object $instance - The check instance.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function list_actions( $instance ) {
|
||||
foreach ( $instance->actions as $action ) {
|
||||
$confirm = isset( $action['confirm'] ) ? $action['confirm'] : false;
|
||||
$button_text = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? esc_html__( 'Running', 'wpscan' ) : esc_html( $action['title'] );
|
||||
$button_disabled = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? ' disabled' : '';
|
||||
|
||||
echo sprintf(
|
||||
"<button class='button' data-check-id='%s' data-confirm='%s' data-action='%s'%s> %s</button>",
|
||||
esc_attr( $instance->id ),
|
||||
esc_attr( $confirm ),
|
||||
esc_attr( $action['id'] ),
|
||||
$button_disabled,
|
||||
$button_text
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get actions buttons
|
||||
*
|
||||
* @param object $instance - The check instance.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.14.4
|
||||
*
|
||||
*/
|
||||
public function get_list_actions( $instance ) {
|
||||
foreach ( $instance->actions as $action ) {
|
||||
$confirm = isset( $action['confirm'] ) ? $action['confirm'] : false;
|
||||
$button_text = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? esc_html__( 'Running', 'wpscan' ) : esc_html( $action['title'] );
|
||||
$button_disabled = ( $this->current_running && array_key_exists( $instance->id, $this->current_running ) && 'dismiss' !== $action['id'] ) ? ' disabled' : '';
|
||||
|
||||
return sprintf(
|
||||
"<button class='button' data-check-id='%s' data-confirm='%s' data-action='%s'%s> %s</button>",
|
||||
esc_attr( $instance->id ),
|
||||
esc_attr( $confirm ),
|
||||
esc_attr( $action['id'] ),
|
||||
$button_disabled,
|
||||
$button_text
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load checks files.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function handle_actions() {
|
||||
check_ajax_referer( 'wpscan' );
|
||||
|
||||
if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$check = isset( $_POST['check'] ) ? $_POST['check'] : false;
|
||||
$action = isset( $_POST['action_id'] ) ? $_POST['action_id'] : false;
|
||||
|
||||
if ( $action && $check ) {
|
||||
$res = 0;
|
||||
if ( 'run' === $action ) {
|
||||
$event_type[ $check ] = $action;
|
||||
$this->add_event_inline( $event_type );
|
||||
|
||||
if ( false === as_next_scheduled_action( $this->WPSCAN_SECURITY_SCHEDULE ) ) {
|
||||
as_schedule_single_action( strtotime( 'now' ), $this->WPSCAN_SECURITY_SCHEDULE );
|
||||
}
|
||||
$res = 1;
|
||||
} else {
|
||||
$action = array_filter(
|
||||
$this->checks[ $check ]['instance']->actions,
|
||||
function ( $i ) use ( $action ) {
|
||||
return $i['id'] === $action;
|
||||
}
|
||||
);
|
||||
|
||||
$action = current( $action );
|
||||
|
||||
if ( method_exists( $this->checks[ $check ]['instance'], $action['method'] ) ) {
|
||||
$res = call_user_func( array( $this->checks[ $check ]['instance'], $action['method'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $res ) {
|
||||
wp_send_json_success( $check );
|
||||
} else {
|
||||
wp_send_json_error();
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Security checks
|
||||
*
|
||||
* @since 1.15
|
||||
* @acces public
|
||||
*/
|
||||
public function security_check_now() {
|
||||
if ( ! empty( $this->current_running ) ) {
|
||||
foreach ( $this->current_running as $key => $to_check ) {
|
||||
$check = $key;
|
||||
$action = $to_check;
|
||||
$action = array_filter(
|
||||
$this->checks[ $check ]['instance']->actions,
|
||||
function ( $i ) use ( $action ) {
|
||||
return $i['id'] === $action;
|
||||
}
|
||||
);
|
||||
|
||||
$action = current( $action );
|
||||
|
||||
if ( method_exists( $this->checks[ $check ]['instance'], $action['method'] ) ) {
|
||||
call_user_func( array( $this->checks[ $check ]['instance'], $action['method'] ) );
|
||||
}
|
||||
$this->remove_event_from_list( $check );
|
||||
as_schedule_single_action( strtotime( 'now' ) + 10, $this->WPSCAN_SECURITY_SCHEDULE );
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
delete_option( $this->OPT_EVENTS_INLINE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register event to wait inline
|
||||
*
|
||||
* @param $event_type
|
||||
*
|
||||
* @since 1.15
|
||||
* @acces public
|
||||
*/
|
||||
public function add_event_inline( $event_type ) {
|
||||
if ( $this->current_running ) {
|
||||
update_option( $this->OPT_EVENTS_INLINE, $this->current_running + $event_type );
|
||||
} else {
|
||||
update_option( $this->OPT_EVENTS_INLINE, $event_type );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event from the waiting line
|
||||
*
|
||||
* @param $event
|
||||
*
|
||||
* @since 1.15
|
||||
* @acces public
|
||||
*/
|
||||
public function remove_event_from_list( $event ) {
|
||||
if ( $event ) {
|
||||
unset( $this->current_running[ $event ] );
|
||||
update_option( $this->OPT_EVENTS_INLINE, $this->current_running );
|
||||
}
|
||||
}
|
||||
}
|
84
wp-content/plugins/wpscan/app/Dashboard.php
Normal file
84
wp-content/plugins/wpscan/app/Dashboard.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Dashboard.
|
||||
*
|
||||
* Used for the dashboard widgets.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Dashboard {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param object $parent parent.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
|
||||
add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widgets' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the widget
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function add_dashboard_widgets() {
|
||||
if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_add_dashboard_widget(
|
||||
$this->parent->WPSCAN_DASHBOARD,
|
||||
__( 'WPScan Status', 'wpscan' ),
|
||||
array( $this, 'dashboard_widget_content' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the widget
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function dashboard_widget_content() {
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
if ( ! $this->parent->classes['settings']->api_token_set() ) {
|
||||
echo esc_html( '<div>' . __( 'To use WPScan you have to setup your WPScan API Token.', 'wpscan' ) . '</div>' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $report ) ) {
|
||||
echo esc_html( __( 'No Report available', 'wpscan' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$vulns = $this->parent->classes['report']->get_all_vulnerabilities();
|
||||
|
||||
if ( empty( $vulns ) ) {
|
||||
echo esc_html( __( 'No vulnerabilities found', 'wpscan' ) );
|
||||
}
|
||||
|
||||
echo '<div>';
|
||||
|
||||
foreach ( $vulns as $vuln ) {
|
||||
$vuln = wp_kses( $vuln, array( 'a' => array( 'href' => array() ) ) ); // Only allow a href HTML tags.
|
||||
echo "<div><span class='dashicons dashicons-warning is-red'></span> " . $vuln . "</div><br/>";
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
347
wp-content/plugins/wpscan/app/Notification.php
Normal file
347
wp-content/plugins/wpscan/app/Notification.php
Normal file
@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Notification.
|
||||
*
|
||||
* Used for the Notifications logic.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Notification {
|
||||
// Page slug.
|
||||
private $page;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param object $parent parent.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($parent) {
|
||||
$this->parent = $parent;
|
||||
$this->page = 'wpscan_notification';
|
||||
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_init', array( $this, 'add_meta_box_notification' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification Options
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function admin_init() {
|
||||
$total = $this->parent->get_total();
|
||||
|
||||
register_setting( $this->page, $this->parent->OPT_EMAIL, array( $this, 'sanitize_email' ) );
|
||||
register_setting( $this->page, $this->parent->OPT_INTERVAL, array( $this, 'sanitize_interval' ) );
|
||||
|
||||
$section = $this->page . '_section';
|
||||
|
||||
add_settings_section(
|
||||
$section,
|
||||
null,
|
||||
array( $this, 'introduction' ),
|
||||
$this->page
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_EMAIL,
|
||||
__( 'E-mail', 'wpscan' ),
|
||||
array( $this, 'field_email' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_INTERVAL,
|
||||
__( 'Send Alerts', 'wpscan' ),
|
||||
array( $this, 'field_interval' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function add_meta_box_notification() {
|
||||
add_meta_box(
|
||||
'wpscan-metabox-notification',
|
||||
__( 'Notification', 'wpscan' ),
|
||||
array( $this, 'do_meta_box_notification' ),
|
||||
'wpscan',
|
||||
'side',
|
||||
'low'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function do_meta_box_notification() {
|
||||
echo '<form action="options.php" method="post">';
|
||||
|
||||
settings_fields( $this->page );
|
||||
|
||||
do_settings_sections( $this->page );
|
||||
|
||||
submit_button();
|
||||
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Introduction
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function introduction() {
|
||||
echo '<p>' . __( 'Fill in the options below if you want to be notified by mail about new vulnerabilities. To add multiple e-mail addresses comma separate them.', 'wpscan' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Email field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_email()
|
||||
{
|
||||
echo sprintf(
|
||||
'<input type="text" name="%s" value="%s" class="regular-text" placeholder="email@domain.com, copy@domain.com">',
|
||||
esc_attr( $this->parent->OPT_EMAIL ),
|
||||
esc_attr( get_option( $this->parent->OPT_EMAIL, '' ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interval field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_interval() {
|
||||
$interval = get_option( $this->parent->OPT_INTERVAL, 'd' );
|
||||
|
||||
echo '<select name="' . $this->parent->OPT_INTERVAL . '">';
|
||||
echo '<option value="o" ' . selected( 'o', $interval, false ) . '>' . __( 'Disabled', 'wpscan' ) . '</option>';
|
||||
echo '<option value="d" ' . selected( 'd', $interval, false ) . '>' . __( 'Daily', 'wpscan' ) . '</option>';
|
||||
echo '<option value="1" ' . selected( 1, $interval, false ) . '>' . __( 'Every Monday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="2" ' . selected( 2, $interval, false ) . '>' . __( 'Every Tuesday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="3" ' . selected( 3, $interval, false ) . '>' . __( 'Every Wednesday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="4" ' . selected( 4, $interval, false ) . '>' . __( 'Every Thursday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="5" ' . selected( 5, $interval, false ) . '>' . __( 'Every Friday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="6" ' . selected( 6, $interval, false ) . '>' . __( 'Every Saturday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="7" ' . selected( 7, $interval, false ) . '>' . __( 'Every Sunday', 'wpscan' ) . '</option>';
|
||||
echo '<option value="m" ' . selected( 'm', $interval, false ) . '>' . __( 'Every Month', 'wpscan' ) . '</option>';
|
||||
echo '</selected>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize email
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_email( $value ) {
|
||||
if ( ! empty( $value ) ) {
|
||||
$emails = explode( ',', $value );
|
||||
|
||||
foreach ( $emails as $email ) {
|
||||
if ( ! is_email( trim( $email ) ) ) {
|
||||
add_settings_error( $this->parent->OPT_EMAIL, 'invalid-email', __( 'You have entered an invalid e-mail address.', 'wpscan' ) );
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize interval
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_interval( $value ) {
|
||||
$allowed_values = array( 'o', 'd', 1, 2, 3, 4, 5, 6, 7, 'm' );
|
||||
|
||||
if ( ! in_array( $value, $allowed_values ) ) {
|
||||
// return default value.
|
||||
return 'd';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the notification
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function notify() {
|
||||
if ( ! function_exists( 'get_plugins' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
$email = get_option( $this->parent->OPT_EMAIL );
|
||||
$interval = get_option( $this->parent->OPT_INTERVAL, 'd' );
|
||||
|
||||
// Check email or if notifications are disabled.
|
||||
if ( empty( $email ) || 'o' === $interval ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check weekly interval.
|
||||
if ( is_numeric( $interval ) && date( 'N' ) !== $interval ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check monthly interval.
|
||||
if ( $interval === 'm' && date( 'j' ) !== 1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send email.
|
||||
$has_vulnerabilities = false;
|
||||
|
||||
$msg = '<!doctype html><html><head><meta charset="utf-8"></head><body>';
|
||||
$msg .= '<p>' . __( 'Hello,', 'wpscan' ) . '</p>';
|
||||
$msg .= '<p>' . sprintf(__( 'The %s found some vulnerabilities in %s, listed below.', 'wpscan' ), '<a href="https://wordpress.org/plugins/wpscan/">WPScan WordPress security plugin</a>' , '<a href="' . get_bloginfo( 'url' ) . '">' . get_bloginfo( 'name' ) . '</a>' ) . '</p>';
|
||||
|
||||
// WordPress
|
||||
$list = $this->email_vulnerabilities( 'wordpress' , get_bloginfo( 'version' ));
|
||||
|
||||
if ( ! empty( $list ) ) {
|
||||
$has_vulnerabilities = true;
|
||||
|
||||
$msg .= '<p><b>WordPress</b><br>';
|
||||
$msg .= join( '<br>', $list ) . '</p>';
|
||||
}
|
||||
|
||||
// Plugins.
|
||||
foreach ( get_plugins() as $name => $details ) {
|
||||
$slug = $this->parent->get_plugin_slug( $name, $details );
|
||||
$list = $this->email_vulnerabilities( 'plugins', $slug );
|
||||
|
||||
if ( ! empty( $list ) ) {
|
||||
$has_vulnerabilities = true;
|
||||
|
||||
$msg .= '<p><b>' . __( 'Plugin', 'wpscan' ) . ' ' . esc_html( $details['Name'] ) . '</b><br>';
|
||||
$msg .= join( '<br>', $list ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Themes.
|
||||
foreach ( wp_get_themes() as $name => $details ) {
|
||||
$slug = $this->parent->get_theme_slug( $name, $details );
|
||||
$list = $this->email_vulnerabilities( 'themes', $slug );
|
||||
|
||||
if ( ! empty( $list ) ) {
|
||||
$has_vulnerabilities = true;
|
||||
|
||||
$msg .= '<p><b>' . __( 'Theme', 'wpscan' ) . ' ' . esc_html( $details['Name'] ) . '</b><br>';
|
||||
$msg .= join( '<br>', $list ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// Security checks.
|
||||
foreach ( $this->parent->classes['checks/system']->checks as $id => $data ) {
|
||||
$list = $this->email_vulnerabilities( 'security-checks', $id );
|
||||
|
||||
if ( ! empty( $list ) ) {
|
||||
$has_vulnerabilities = true;
|
||||
|
||||
$msg .= '<p><b>' . __( 'Security check', 'wpscan' ) . ' ' . esc_html( $data['instance']->title() ) . '</b><br>';
|
||||
$msg .= join( '<br>', $list ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
$msg .= '<p>' . sprintf(__( 'Found our WPScan security plugin helpful? Please %s', 'wpscan' ), '<a href="https://wordpress.org/support/plugin/wpscan/reviews/#new-post">leave a review.</a></p>');
|
||||
|
||||
$msg .= '<p>' . __( 'Thank you,', 'wpscan' ) . '<br/>' . __( 'The WPScan Team', 'wpscan' ) . '</p>';
|
||||
|
||||
$msg .= '</body></html>';
|
||||
|
||||
if ( $has_vulnerabilities ) {
|
||||
$subject = sprintf(
|
||||
__( '[WPScan Alert] Some vulnerabilities were found in %s!', 'wpscan' ),
|
||||
get_bloginfo( 'name' )
|
||||
);
|
||||
|
||||
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
|
||||
|
||||
wp_mail( $email, $subject, $msg, $headers );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of vulnerabilities to send by mail
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function email_vulnerabilities( $type, $name ) {
|
||||
$report = $this->parent->get_report()[ $type ];
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
if ( array_key_exists( $name, $report ) ) {
|
||||
$report = $report[ $name ];
|
||||
}
|
||||
|
||||
if ( ! isset( $report['vulnerabilities'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ( $report['vulnerabilities'] as $item ) {
|
||||
$id = 'security-checks' === $type ? $item['id'] : $item->id;
|
||||
$title = 'security-checks' === $type ? $item['title'] : $this->parent->get_sanitized_vulnerability_title( $item );
|
||||
|
||||
if ( in_array( $id, $ignored ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'security-checks' !== $type ) {
|
||||
$html = '<a href="' . esc_url( 'https://wpscan.com/vulnerability/' . $id ) . '" target="_blank">';
|
||||
$html .= esc_html( $title );
|
||||
$html .= '</a>';
|
||||
} else {
|
||||
$html = esc_html( $title );
|
||||
}
|
||||
|
||||
$list[] = $html;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
907
wp-content/plugins/wpscan/app/Plugin.php
Normal file
907
wp-content/plugins/wpscan/app/Plugin.php
Normal file
@ -0,0 +1,907 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Plugin.
|
||||
*
|
||||
* General WPScan plugin logic.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Plugin {
|
||||
// Settings.
|
||||
public $OPT_API_TOKEN = 'wpscan_api_token';
|
||||
public $OPT_SCANNING_INTERVAL = 'wpscan_scanning_interval';
|
||||
public $OPT_SCANNING_TIME = 'wpscan_scanning_time';
|
||||
public $OPT_IGNORE_ITEMS = 'wpscan_ignore_items';
|
||||
|
||||
// Account.
|
||||
public $OPT_ACCOUNT_STATUS = 'wpscan_account_status';
|
||||
|
||||
// Notifications.
|
||||
public $OPT_EMAIL = 'wpscan_mail';
|
||||
public $OPT_INTERVAL = 'wpscan_interval';
|
||||
public $OPT_IGNORED = 'wpscan_ignored';
|
||||
|
||||
// Report.
|
||||
public $OPT_REPORT = 'wpscan_report';
|
||||
public $OPT_ERRORS = 'wpscan_errors';
|
||||
|
||||
// Schedule.
|
||||
public $WPSCAN_SCHEDULE = 'wpscan_schedule';
|
||||
|
||||
// Run All schedule.
|
||||
public $WPSCAN_RUN_ALL = 'wpscan_run_all';
|
||||
|
||||
// Run Security schedule.
|
||||
public $WPSCAN_RUN_SECURITY = 'wpscan_events_inline';
|
||||
|
||||
// Dashboard.
|
||||
public $WPSCAN_DASHBOARD = 'wpscan_dashboard';
|
||||
|
||||
// Transient.
|
||||
public $WPSCAN_TRANSIENT_CRON = 'wpscan_doing_cron';
|
||||
|
||||
// Required minimal role.
|
||||
public $WPSCAN_ROLE = 'manage_options';
|
||||
|
||||
// Plugin path.
|
||||
public $plugin_dir = '';
|
||||
|
||||
// Plugin URI.
|
||||
public $plugin_url = '';
|
||||
|
||||
// Page.
|
||||
public $page_hook = 'toplevel_page_wpscan';
|
||||
|
||||
// Report.
|
||||
public $report;
|
||||
|
||||
// Action fired when an issue is found
|
||||
public $WPSCAN_ISSUE_FOUND = 'wpscan_issue_found';
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->plugin_dir = trailingslashit( str_replace( '\\', '/', dirname( WPSCAN_PLUGIN_FILE ) ) );
|
||||
$this->plugin_url = site_url( str_replace( str_replace( '\\', '/', ABSPATH ), '', $this->plugin_dir ) );
|
||||
|
||||
// Languages.
|
||||
load_plugin_textdomain( 'wpscan', false, $this->plugin_dir . 'languages' );
|
||||
|
||||
// Cache values in memory.
|
||||
$this->report = get_option( $this->OPT_REPORT, array() );
|
||||
$this->ignored_vulnerabilities = get_option( $this->OPT_IGNORED, array() );
|
||||
|
||||
// Actions.
|
||||
add_action( 'admin_menu', array( $this, 'menu' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
|
||||
add_action( 'admin_bar_menu', array( $this, 'admin_bar' ), 65 );
|
||||
add_action( $this->WPSCAN_SCHEDULE, array( $this, 'check_now' ) );
|
||||
add_action( $this->WPSCAN_RUN_ALL, array( $this, 'check_now' ) );
|
||||
add_action( 'in_admin_header', array( $this, 'deactivate_screen' ) );
|
||||
|
||||
if ( defined( 'WPSCAN_API_TOKEN' ) ) {
|
||||
add_action( 'admin_init', array( $this, 'api_token_from_constant' ) );
|
||||
}
|
||||
|
||||
// Filters.
|
||||
add_filter( 'plugin_action_links_' . plugin_basename( WPSCAN_PLUGIN_FILE ), array( $this, 'add_action_links' ) );
|
||||
|
||||
// Micro apps (modules).
|
||||
$this->classes['report'] = new Report( $this );
|
||||
$this->classes['settings'] = new Settings( $this );
|
||||
$this->classes['account'] = new Account( $this );
|
||||
$this->classes['notification'] = new Notification( $this );
|
||||
$this->classes['summary'] = new Summary( $this );
|
||||
$this->classes['ignoreVulnerabilities'] = new ignoreVulnerabilities( $this );
|
||||
$this->classes['dashboard'] = new Dashboard( $this );
|
||||
$this->classes['sitehealth'] = new SiteHealth( $this );
|
||||
$this->classes['checks/system'] = new Checks\System( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin Loaded
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function loaded() {
|
||||
load_plugin_textdomain( 'wpscan', false, $this->plugin_dir . 'languages' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate actions. Runs when the plugin is activated.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function activate() {
|
||||
$this->delete_doing_cron_transient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate actions
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function deactivate() {
|
||||
delete_option( $this->OPT_SCANNING_INTERVAL );
|
||||
|
||||
delete_option( $this->OPT_SCANNING_TIME );
|
||||
|
||||
as_unschedule_all_actions( $this->WPSCAN_SCHEDULE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate screen
|
||||
*
|
||||
* @return void
|
||||
* @since 1.14.0
|
||||
* @access public
|
||||
*/
|
||||
public function deactivate_screen() {
|
||||
global $pagenow;
|
||||
|
||||
if ( 'plugins.php' === $pagenow ) {
|
||||
include_once plugin_dir_path( WPSCAN_PLUGIN_FILE ) . 'views/deactivate.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the global constant WPSCAN_API_TOKEN if defined.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @example define('WPSCAN_API_TOKEN', 'xxx');
|
||||
*
|
||||
*/
|
||||
public function api_token_from_constant() {
|
||||
if ( get_option( $this->OPT_API_TOKEN ) !== WPSCAN_API_TOKEN ) {
|
||||
$sanitize = $this->classes['settings']->sanitize_api_token( WPSCAN_API_TOKEN );
|
||||
|
||||
if ( $sanitize ) {
|
||||
update_option( $this->OPT_API_TOKEN, WPSCAN_API_TOKEN );
|
||||
} else {
|
||||
delete_option( $this->OPT_API_TOKEN );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Admin Scripts
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function admin_enqueue( $hook ) {
|
||||
global $pagenow;
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( $hook === $this->page_hook || 'dashboard' === $screen->id ) {
|
||||
wp_enqueue_style(
|
||||
'wpscan',
|
||||
plugins_url( 'assets/css/style.css', WPSCAN_PLUGIN_FILE ),
|
||||
array(),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
}
|
||||
|
||||
if ( $hook === $this->page_hook ) {
|
||||
wp_enqueue_script( 'common' );
|
||||
wp_enqueue_script( 'wp-lists' );
|
||||
wp_enqueue_script( 'postbox' );
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpscan',
|
||||
plugins_url( 'assets/js/scripts.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'jquery' ),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpscan-download-report',
|
||||
plugins_url( 'assets/js/download-report.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'pdfmake' ),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
|
||||
|
||||
wp_enqueue_script(
|
||||
'pdfmake',
|
||||
plugins_url( 'assets/vendor/pdfmake/pdfmake.min.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'wpscan' ),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpscan-download-report-fonts',
|
||||
plugins_url( 'assets/vendor/pdfmake/vfs_fonts.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'wpscan' ),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
|
||||
$localized = array(
|
||||
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
||||
'action_check' => 'wpscan_check_now',
|
||||
'action_security_check' => 'wpscan_security_check_now',
|
||||
'action_cron' => $this->WPSCAN_TRANSIENT_CRON,
|
||||
'ajax_nonce' => wp_create_nonce( 'wpscan' ),
|
||||
'doing_cron' => false !== as_next_scheduled_action( $this->WPSCAN_RUN_ALL ) ? 'YES' : 'NO',
|
||||
'doing_security_cron' => get_option( $this->WPSCAN_RUN_SECURITY ),
|
||||
'running' => esc_html__( 'Running', 'wpscan' ),
|
||||
'not_running' => esc_html__( 'Run', 'wpscan' ),
|
||||
);
|
||||
|
||||
wp_localize_script( 'wpscan', 'wpscan', $localized );
|
||||
}
|
||||
|
||||
if ( 'plugins.php' === $pagenow ) {
|
||||
wp_enqueue_style(
|
||||
'wpscan-deactivate',
|
||||
plugins_url( 'assets/css/deactivate.css', WPSCAN_PLUGIN_FILE ),
|
||||
array(),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wpscan-deactivate',
|
||||
plugins_url( 'assets/js/deactivate.js', WPSCAN_PLUGIN_FILE ),
|
||||
array( 'jquery' ),
|
||||
$this->wpscan_plugin_version()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest report
|
||||
*
|
||||
* @return array|bool
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_report() {
|
||||
if ( ! empty( $this->report ) ) {
|
||||
return ( $this->report );
|
||||
}
|
||||
|
||||
return get_option( $this->OPT_REPORT, array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest report
|
||||
*
|
||||
* @return array|bool
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_ignored_vulnerabilities() {
|
||||
if ( ! empty( $this->ignored_vulnerabilities ) ) {
|
||||
return ( $this->ignored_vulnerabilities );
|
||||
}
|
||||
|
||||
return get_option( $this->OPT_IGNORED, array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of vulnerabilities found or -1 if errors
|
||||
*
|
||||
* @return int
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_total() {
|
||||
$report = get_option( $this->OPT_REPORT );
|
||||
|
||||
if ( empty( $report ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
|
||||
foreach ( array( 'wordpress', 'themes', 'plugins', 'security-checks' ) as $type ) {
|
||||
if ( isset( $report[ $type ] ) ) {
|
||||
if ( isset( $report[ $type ]['total'] ) ) {
|
||||
unset( $report[ $type ]['total'] );
|
||||
}
|
||||
|
||||
foreach ( $report[ $type ] as $slug => $item ) {
|
||||
$p = $report[ $type ][ $slug ];
|
||||
|
||||
if ( isset( $p['vulnerabilities'] ) && is_array( $p['vulnerabilities'] ) ) {
|
||||
$total += count( $p['vulnerabilities'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total of vulnerabilities found but not ignored
|
||||
*
|
||||
* @return int
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function get_total_not_ignored() {
|
||||
$report = $this->get_report();
|
||||
$ignored = get_option( $this->OPT_IGNORED, array() );
|
||||
|
||||
$total = $this->get_total();
|
||||
|
||||
$types = array( 'wordpress', 'plugins', 'themes', 'security-checks' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( isset( $report[ $type ] ) ) {
|
||||
foreach ( $report[ $type ] as $item ) {
|
||||
if ( empty( $item['vulnerabilities'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $item['vulnerabilities'] as $vuln ) {
|
||||
$id = 'security-checks' === $type ? $vuln['id'] : $vuln->id;
|
||||
|
||||
if ( in_array( $id, $ignored, true ) ) {
|
||||
-- $total;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a menu on Tools section
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function menu() {
|
||||
$total = $this->get_total_not_ignored();
|
||||
$count = $total > 0 ? ' <span class="update-plugins">' . $total . '</span>' : null;
|
||||
|
||||
add_menu_page(
|
||||
'WPScan',
|
||||
'WPScan' . $count,
|
||||
$this->WPSCAN_ROLE,
|
||||
'wpscan',
|
||||
array( $this->classes['report'], 'page' ),
|
||||
$this->plugin_url . 'assets/svg/menu-icon.svg',
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include a shortcut on Plugins Page
|
||||
*
|
||||
* @param array $links - Array of links provided by the filter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function add_action_links( $links ) {
|
||||
$links[] = '<a href="' . admin_url( 'admin.php?page=wpscan' ) . '">' . __( 'View' ) . '</a>';
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WPScan plugin version.
|
||||
*
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function wpscan_plugin_version() {
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
return get_plugin_data( $this->plugin_dir . 'wpscan.php' )['Version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information from the API
|
||||
*
|
||||
* @return object|int the JSON object or the response code.
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function api_get( $endpoint, $api_token = null ) {
|
||||
if ( empty( $api_token ) ) {
|
||||
$api_token = get_option( $this->OPT_API_TOKEN );
|
||||
}
|
||||
|
||||
// Make sure endpoint starts with a slash.
|
||||
if ( substr( $endpoint, 0, 1 ) !== '/' ) {
|
||||
$endpoint = '/' . $endpoint;
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Token token=' . $api_token,
|
||||
'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . home_url() . ' WPScan/' . $this->wpscan_plugin_version(),
|
||||
),
|
||||
);
|
||||
|
||||
// Hook before the request.
|
||||
//do_action( 'wpscan/api/get/before', $endpoint );
|
||||
|
||||
// Start the request.
|
||||
$response = wp_remote_get( WPSCAN_API_URL . $endpoint, $args );
|
||||
$code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
// Hook after the request.
|
||||
//do_action( 'wpscan/api/get/after', $endpoint, $response );
|
||||
|
||||
if ( 200 === $code ) {
|
||||
return json_decode( wp_remote_retrieve_body( $response ) );
|
||||
} else {
|
||||
$errors = get_option( $this->OPT_ERRORS, array() );
|
||||
|
||||
switch ( $code ) {
|
||||
case 401:
|
||||
array_push( $errors, __( 'Your API Token expired', 'wpscan' ) );
|
||||
break;
|
||||
case 403:
|
||||
array_push( $errors, __( 'You have entered an invalid API Token', 'wpscan' ) );
|
||||
break;
|
||||
case 404:
|
||||
// We don't have the plugin/theme, do nothing.
|
||||
break;
|
||||
case 429:
|
||||
array_push( $errors, sprintf( '%s <a href="%s" target="_blank">%s</a>.', __( 'You hit our free API usage limit. To increase your daily API limit please upgrade to paid usage from your', 'wpscan' ), WPSCAN_PROFILE_URL, __( 'WPScan profile page', 'wpscan' ) ) );
|
||||
break;
|
||||
case 500:
|
||||
array_push( $errors, sprintf( '%s <a href="%s" target="_blank">%s</a>', __( 'There seems to be a problem with the WPScan API. Status: 500. Check the ', 'wpscan' ), WPSCAN_STATUS_URL, __( 'API Status', 'wpscan' ) ) );
|
||||
break;
|
||||
case 502:
|
||||
array_push( $errors, sprintf( '%s <a href="%s" target="_blank">%s</a>', __( 'There seems to be a problem with the WPScan API. Status: 502. Check the ', 'wpscan' ), WPSCAN_STATUS_URL, __( 'API Status', 'wpscan' ) ) );
|
||||
break;
|
||||
case '':
|
||||
array_push( $errors, sprintf( '%s <a href="%s" target="_blank">%s</a>', __( 'We were unable to connect to the WPScan API. Check the ', 'wpscan' ), WPSCAN_STATUS_URL, __( 'API Status', 'wpscan' ) ) );
|
||||
break;
|
||||
default:
|
||||
array_push( $errors, sprintf( '%s <a href="%s" target="_blank">%s</a>.', __( 'We received an unknown response from the API. Status: ' . esc_html( $code ), 'wpscan' ), WPSCAN_STATUS_URL, __( 'Check API Status', 'wpscan' ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
// Save the errors.
|
||||
update_option( $this->OPT_ERRORS, array_unique( $errors ) );
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to start checking right now
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function check_now() {
|
||||
if ( get_transient( $this->WPSCAN_TRANSIENT_CRON ) || empty( get_option( $this->OPT_API_TOKEN ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start cron job and set timeout.
|
||||
set_transient( $this->WPSCAN_TRANSIENT_CRON, time(), 60 );
|
||||
|
||||
$this->verify();
|
||||
|
||||
delete_transient( $this->WPSCAN_TRANSIENT_CRON );
|
||||
|
||||
// Notify by mail when solicited.
|
||||
$this->classes['notification']->notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to verify on WpScan Database for vulnerabilities
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function verify() {
|
||||
$ignored = get_option( $this->OPT_IGNORE_ITEMS );
|
||||
|
||||
$ignored = wp_parse_args(
|
||||
$ignored,
|
||||
array(
|
||||
'plugins' => array(),
|
||||
'themes' => array(),
|
||||
)
|
||||
);
|
||||
|
||||
// Reset errors.
|
||||
update_option( $this->OPT_ERRORS, array() );
|
||||
update_option( $this->classes['checks/system']->OPT_FATAL_ERRORS, array() );
|
||||
|
||||
// Plugins.
|
||||
$this->report['plugins'] = $this->verify_plugins( $ignored['plugins'] );
|
||||
|
||||
// Themes.
|
||||
$this->report['themes'] = $this->verify_themes( $ignored['themes'] );
|
||||
|
||||
// WordPress.
|
||||
if ( ! isset( $ignored['wordpress'] ) ) {
|
||||
$this->report['wordpress'] = $this->verify_wordpress();
|
||||
} else {
|
||||
$this->report['wordpress'] = array();
|
||||
}
|
||||
|
||||
// Security checks.
|
||||
$this->report['security-checks'] = array();
|
||||
|
||||
foreach ( $this->classes['checks/system']->checks as $id => $data ) {
|
||||
$data['instance']->perform();
|
||||
$this->report['security-checks'][ $id ]['vulnerabilities'] = array();
|
||||
|
||||
if ( $data['instance']->vulnerabilities ) {
|
||||
$this->report['security-checks'][ $id ]['vulnerabilities'] = $data['instance']->get_vulnerabilities();
|
||||
|
||||
$this->maybe_fire_issue_found_action( 'security-check', $id, $this->report['security-checks'][ $id ] );
|
||||
}
|
||||
}
|
||||
|
||||
// Caching.
|
||||
$this->report['cache'] = strtotime( current_time( 'mysql' ) );
|
||||
|
||||
// Saving.
|
||||
update_option( $this->OPT_REPORT, $this->report, true );
|
||||
|
||||
// Updates account status (API calls etc).
|
||||
$this->classes['account']->update_account_status();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires the wpscan_issue_found action if needed
|
||||
*
|
||||
* @param string $type - The affected component type: plugin, theme, WordPress or security-check
|
||||
* @param string $slug - The affected component slug.
|
||||
* For WordPress, it will be the version (ie 5.5.3)
|
||||
* For security-checks, it will be the id of the check, ie xmlrpc-enabled
|
||||
* @param array $details - An array containing some keys, such as vulnerabilities
|
||||
* @param array additional_details - An array with the plugin details, such as Version etc
|
||||
**@since 1.14.0
|
||||
*
|
||||
*/
|
||||
public function maybe_fire_issue_found_action( $type, $slug, $details, $additional_details = array() ) {
|
||||
if ( ! count( $details['vulnerabilities'] ) > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_action( $this->WPSCAN_ISSUE_FOUND, $type, $slug, $details, $additional_details );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check plugins for any known vulnerabilities
|
||||
*
|
||||
* @param array $ignored - An array of plugins slug to ignore
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function verify_plugins( $ignored ) {
|
||||
$plugins = array();
|
||||
|
||||
if ( ! function_exists( 'get_plugins' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
foreach ( get_plugins() as $name => $details ) {
|
||||
$slug = $this->get_plugin_slug( $name, $details );
|
||||
|
||||
if ( isset( $ignored[ $slug ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->api_get( '/plugins/' . $slug );
|
||||
|
||||
if ( is_object( $result ) ) {
|
||||
$plugins[ $slug ]['vulnerabilities'] = $this->get_vulnerabilities( $result, $details['Version'] );
|
||||
|
||||
if ( isset( $result->$slug->closed ) ) {
|
||||
$plugins[ $slug ]['closed'] = is_object( $result->$slug->closed ) ? true : false;
|
||||
} else {
|
||||
$plugins[ $slug ]['closed'] = false;
|
||||
}
|
||||
|
||||
$this->maybe_fire_issue_found_action( 'plugin', $slug, $plugins[ $slug ], $details );
|
||||
} else {
|
||||
if ( 404 === $result ) {
|
||||
$plugins[ $slug ]['not_found'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check themes for any known vulnerabilities
|
||||
*
|
||||
* @param array $ignored - An array of themes slug to ignore.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function verify_themes( $ignored ) {
|
||||
$themes = array();
|
||||
|
||||
if ( ! function_exists( 'wp_get_themes' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/theme.php';
|
||||
}
|
||||
|
||||
$filter = array(
|
||||
'errors' => null,
|
||||
'allowed' => null,
|
||||
'blog_id' => 0,
|
||||
);
|
||||
|
||||
foreach ( wp_get_themes( $filter ) as $name => $details ) {
|
||||
$slug = $this->get_theme_slug( $name, $details );
|
||||
|
||||
if ( isset( $ignored[ $slug ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->api_get( '/themes/' . $slug );
|
||||
|
||||
if ( is_object( $result ) ) {
|
||||
$themes[ $slug ]['vulnerabilities'] = $this->get_vulnerabilities( $result, $details['Version'] );
|
||||
|
||||
if ( isset( $result->$slug->closed ) ) {
|
||||
$themes[ $slug ]['closed'] = is_object( $result->$slug->closed ) ? true : false;
|
||||
} else {
|
||||
$themes[ $slug ]['closed'] = false;
|
||||
}
|
||||
|
||||
$this->maybe_fire_issue_found_action( 'theme', $slug, $themes[ $slug ], $details );
|
||||
} else {
|
||||
if ( 404 === $result ) {
|
||||
$themes[ $slug ]['not_found'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check WordPress for any known vulnerabilities.
|
||||
*
|
||||
* @return array
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function verify_wordpress() {
|
||||
$wordpress = array();
|
||||
|
||||
$version = get_bloginfo( 'version' );
|
||||
$result = $this->api_get( '/wordpresses/' . str_replace( '.', '', $version ) );
|
||||
|
||||
if ( is_object( $result ) ) {
|
||||
$wordpress[ $version ]['vulnerabilities'] = $this->get_vulnerabilities( $result, $version );
|
||||
|
||||
$this->maybe_fire_issue_found_action( 'WordPress', $version, $wordpress[ $version ] );
|
||||
}
|
||||
|
||||
return $wordpress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter vulnerability list from WPScan
|
||||
*
|
||||
* @param array $data - Report data for the element to check.
|
||||
* @param string $version - Installed version.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function get_vulnerabilities( $data, $version ) {
|
||||
$list = array();
|
||||
$key = key( $data );
|
||||
|
||||
if ( empty( $data->$key->vulnerabilities ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
// Trim and remove potential leading 'v'.
|
||||
$version = ltrim( trim( $version ), 'v' );
|
||||
|
||||
foreach ( $data->$key->vulnerabilities as $item ) {
|
||||
if ( $item->fixed_in ) {
|
||||
if ( version_compare( $version, $item->fixed_in, '<' ) ) {
|
||||
$list[] = $item;
|
||||
}
|
||||
} else {
|
||||
$list[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vulnerability title.
|
||||
*
|
||||
* @param string $vulnerability - element array.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function get_sanitized_vulnerability_title( $vulnerability ) {
|
||||
$title = esc_html( $vulnerability->title ) . ' - ';
|
||||
|
||||
$title .= empty( $vulnerability->fixed_in )
|
||||
? __( 'Not fixed', 'wpscan' )
|
||||
: sprintf( __( 'Fixed in version %s', 'wpscan' ), esc_html( $vulnerability->fixed_in ) );
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin slug for the given name
|
||||
*
|
||||
* @param string $name - plugin name "folder/file.php" or "hello.php".
|
||||
* @param string $details details.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function get_plugin_slug( $name, $details ) {
|
||||
$name = $this->get_name( $name );
|
||||
$name = $this->get_real_slug( $name, $details['PluginURI'] );
|
||||
|
||||
return sanitize_title( $name );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the theme slug for the given name.
|
||||
*
|
||||
* @param string $name - plugin name "folder/file.php" or "hello.php".
|
||||
* @param string $details details.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
public function get_theme_slug( $name, $details ) {
|
||||
$name = $this->get_name( $name );
|
||||
$name = $this->get_real_slug( $name, $details['ThemeURI'] );
|
||||
|
||||
return sanitize_title( $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin/theme name
|
||||
*
|
||||
* @param string $name - plugin name "folder/file.php" or "hello.php".
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
private function get_name( $name ) {
|
||||
return strstr( $name, '/' ) ? dirname( $name ) : $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin/theme slug.
|
||||
*
|
||||
* The name returned by get_plugins or get_themes is not always the real slug.
|
||||
* If the pluginURI is a WordPress url, we take the slug from there.
|
||||
* this also fixes folder renames on plugins if the readme is correct.
|
||||
*
|
||||
* @param string $name - asset name from get_plugins or wp_get_themes.
|
||||
* @param string $url - either the value or ThemeURI or PluginURI.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
*
|
||||
*/
|
||||
private function get_real_slug( $name, $url ) {
|
||||
$slug = $name;
|
||||
$match = preg_match( '/https?:\/\/wordpress\.org\/(?:extend\/)?(?:plugins|themes)\/([^\/]+)\/?/', $url, $matches );
|
||||
|
||||
if ( 1 === $match ) {
|
||||
$slug = $matches[1];
|
||||
}
|
||||
|
||||
return sanitize_title( $slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shortcut on Admin Bar to show the total of vulnerabilities found.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function admin_bar( $wp_admin_bar ) {
|
||||
if ( ! current_user_can( $this->WPSCAN_ROLE ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$total = $this->get_total_not_ignored();
|
||||
|
||||
if ( $total > 0 ) {
|
||||
$args = array(
|
||||
'id' => 'wpscan',
|
||||
'title' => '<span class="ab-icon dashicons-shield"></span><span class="ab-label">' . $total . '</span>',
|
||||
'href' => admin_url( 'admin.php?page=wpscan' ),
|
||||
'meta' => array(
|
||||
'title' => sprintf( _n( '%d vulnerability found', '%d vulnerabilities found', $total, 'wpscan' ), $total ),
|
||||
),
|
||||
);
|
||||
|
||||
$wp_admin_bar->add_node( $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if interval scanning is disabled
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @example define('WPSCAN_DISABLE_SCANNING_INTERVAL', true);
|
||||
*
|
||||
*/
|
||||
public function is_interval_scanning_disabled() {
|
||||
if ( defined( 'WPSCAN_DISABLE_SCANNING_INTERVAL' ) ) {
|
||||
return WPSCAN_DISABLE_SCANNING_INTERVAL;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete doing_cron transient, as they could hang in older versions
|
||||
*
|
||||
* @return void
|
||||
* @since 1.12.2
|
||||
* @access public
|
||||
*/
|
||||
public function delete_doing_cron_transient() {
|
||||
delete_transient( $this->WPSCAN_TRANSIENT_CRON );
|
||||
}
|
||||
}
|
297
wp-content/plugins/wpscan/app/Report.php
Normal file
297
wp-content/plugins/wpscan/app/Report.php
Normal file
@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Report.
|
||||
*
|
||||
* Generates the main report.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Report
|
||||
{
|
||||
// Page slug.
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param object $parent parent.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
$this->page = 'wpscan';
|
||||
|
||||
add_action( 'admin_menu', array( $this, 'menu' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin menu
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function menu() {
|
||||
$title = __( 'Report', 'wpscan' );
|
||||
|
||||
add_submenu_page(
|
||||
'wpscan',
|
||||
$title,
|
||||
$title,
|
||||
$this->parent->WPSCAN_ROLE,
|
||||
$this->page,
|
||||
array( $this, 'page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render report page
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function page() {
|
||||
include $this->parent->plugin_dir . '/views/report.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* List vulnerabilities on screen
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $type - Type of report: WordPress, plugins, themes.
|
||||
* @param string $name - key name of the element.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function list_api_vulnerabilities( $type, $name ) {
|
||||
$report = $this->parent->get_report();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
$null_text = __( 'No known vulnerabilities found to affect this version', 'wpscan' );
|
||||
$not_checked_text = __( 'Not checked yet. Click the Run All button to run a scan', 'wpscan' );
|
||||
|
||||
if ( empty( $report ) || ! isset( $report[ $type ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$report = $report[ $type ];
|
||||
|
||||
if ( array_key_exists( $name, $report ) ) {
|
||||
$report = $report[ $name ];
|
||||
} else {
|
||||
echo esc_html( $not_checked_text );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $report['vulnerabilities'] ) ) {
|
||||
$list = array();
|
||||
|
||||
usort( $report['vulnerabilities'], array( 'self', 'sort_vulnerabilities' ) );
|
||||
|
||||
foreach ( $report['vulnerabilities'] as $item ) {
|
||||
$id = 'security-checks' === $type ? $item['id'] : $item->id;
|
||||
|
||||
if ( in_array( $id, $ignored, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$html = '<div class="vulnerability">';
|
||||
$html .= $this->vulnerability_severity( $item );
|
||||
$html .= '<a href="' . esc_url( 'https://wpscan.com/vulnerability/' . $item->id ) . '" target="_blank">';
|
||||
$html .= $this->parent->get_sanitized_vulnerability_title( $item );
|
||||
$html .= '</a>';
|
||||
$html .= '</div>';
|
||||
|
||||
$list[] = $html;
|
||||
}
|
||||
|
||||
echo empty( $list ) ? $null_text : join( '<br>', $list );
|
||||
|
||||
} else {
|
||||
echo esc_html( $null_text );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort vulnerabilities by severity
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function sort_vulnerabilities( $a, $b ) {
|
||||
$a = isset( $a->cvss->score ) ? intval( $a->cvss->score ) : 0;
|
||||
$b = isset( $b->cvss->score ) ? intval( $b->cvss->score ) : 0;
|
||||
|
||||
return $b > $a ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vulnerability severity
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function vulnerability_severity( $vulnerability ) {
|
||||
$plan = $this->parent->classes['account']->get_account_status()['plan'];
|
||||
|
||||
if ( 'enterprise' !== $plan ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$html = "<div class='vulnerability-severity'>";
|
||||
|
||||
if ( isset( $vulnerability->cvss->severity ) ) {
|
||||
$severity = $vulnerability->cvss->severity;
|
||||
$html .= "<span class='wpscan-" . esc_attr( $severity ) . "'>" . esc_html( $severity ) . '</span>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the plugin/theme is closed
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_item_closed( $type, $name ) {
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
if ( empty( $report ) || ! isset( $report[ $type ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$report = $report[ $type ];
|
||||
|
||||
if ( array_key_exists( $name, $report ) ) {
|
||||
$report = $report[ $name ];
|
||||
}
|
||||
|
||||
return isset( $report['closed'] ) ? $report['closed'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all vulnerabilities
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_vulnerabilities() {
|
||||
$ret = array();
|
||||
$report = $this->parent->get_report();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
$types = array( 'wordpress', 'plugins', 'themes', 'security-checks' );
|
||||
|
||||
foreach ( $types as $type ) {
|
||||
if ( isset( $report[ $type ] ) ) {
|
||||
|
||||
foreach ( $report[ $type ] as $item ) {
|
||||
if ( isset( $item['vulnerabilities'] ) ) {
|
||||
foreach ( $item['vulnerabilities'] as $vuln ) {
|
||||
$id = 'security-checks' === $type ? $vuln['id'] : $vuln->id;
|
||||
$title = 'security-checks' === $type ? $vuln['title'] : $this->parent->get_sanitized_vulnerability_title( $vuln );
|
||||
|
||||
if ( in_array( $id, $ignored, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ret[] = $title;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item non-ignored vulnerabilities count
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $type - Type of report: WordPress, plugins, themes.
|
||||
* @param string $name - key name of the element.
|
||||
*
|
||||
* @access public
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_item_vulnerabilities_count( $type, $name ) {
|
||||
$report = $this->parent->get_report();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
if ( empty( $report ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( isset( $report[ $type ] ) && array_key_exists( $name, $report[ $type ] ) ) {
|
||||
$report = $report[ $type ][ $name ];
|
||||
}
|
||||
|
||||
foreach ( $report['vulnerabilities'] as $key => &$item ) {
|
||||
$id = 'security-checks' === $type ? $item['id'] : $item->id;
|
||||
|
||||
if ( in_array( $id, $ignored, true ) ) {
|
||||
unset( $report['vulnerabilities'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return count( $report['vulnerabilities'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show status icons: checked, attention and error
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $type - Type of report: WordPress, plugins, themes.
|
||||
* @param string $name - key name of the element.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function get_status( $type, $name ) {
|
||||
$report = $this->parent->get_report();
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
if ( empty( $report ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( isset( $report[ $type ] ) && array_key_exists( $name, $report[ $type ] ) ) {
|
||||
$report = $report[ $type ][ $name ];
|
||||
}
|
||||
|
||||
|
||||
if ( array_key_exists( 'not_found', $report ) ) {
|
||||
$icon = 'dashicons-yes is-green';
|
||||
} elseif ( ! isset( $report['vulnerabilities'] ) ) {
|
||||
$icon = 'dashicons-no-alt is-gray';
|
||||
} elseif ( empty( $report['vulnerabilities'] ) ) {
|
||||
$icon = 'dashicons-yes is-green';
|
||||
} else {
|
||||
$count = $this->get_item_vulnerabilities_count( $type, $name );
|
||||
$icon = 0 === $count ? 'dashicons-yes is-green' : 'dashicons-warning is-red';
|
||||
}
|
||||
|
||||
return " <span class='dashicons $icon'></span>";
|
||||
}
|
||||
}
|
495
wp-content/plugins/wpscan/app/Settings.php
Normal file
495
wp-content/plugins/wpscan/app/Settings.php
Normal file
@ -0,0 +1,495 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* Handles the plugin's settings page.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Settings {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
$this->page = 'wpscan_settings';
|
||||
|
||||
add_action( 'admin_menu', array( $this, 'menu' ) );
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_notices', array( $this, 'got_api_token' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue' ) );
|
||||
add_action( 'add_option_' . $this->parent->OPT_SCANNING_INTERVAL, array( $this, 'schedule_event' ), 10, 2 );
|
||||
add_action( 'update_option_' . $this->parent->OPT_SCANNING_INTERVAL, array( $this, 'schedule_event' ), 10, 2 );
|
||||
add_action( 'update_option_' . $this->parent->OPT_SCANNING_TIME, array( $this, 'schedule_event' ), 10, 2 );
|
||||
add_action( 'update_option_' . $this->parent->OPT_IGNORE_ITEMS, array( $this, 'update_ignored_items' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Introduction
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function introduction() {}
|
||||
|
||||
/**
|
||||
* Register Admin Scripts
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function admin_enqueue( $hook ) {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( strstr( $screen->id, $this->page ) ) {
|
||||
wp_enqueue_style(
|
||||
'wpscan-settings',
|
||||
plugins_url( 'assets/css/settings.css', WPSCAN_PLUGIN_FILE ),
|
||||
array(),
|
||||
$this->parent->wpscan_plugin_version()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings Options
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function admin_init() {
|
||||
// No need to sanitise the API token from the Settings form if we are using the token from the
|
||||
// WPSCAN_API_TOKEN constant. register_setting() is run before WPSCAN_API_TOKEN is placed in the database,
|
||||
// causing a NULL API token to be passed to some functions if called when using a WPSCAN_API_TOKEN constant.
|
||||
if ( ! defined( 'WPSCAN_API_TOKEN' ) ) {
|
||||
register_setting( $this->page, $this->parent->OPT_API_TOKEN, array( 'sanitize_callback' => array( $this, 'sanitize_api_token' ) ) );
|
||||
}
|
||||
|
||||
register_setting( $this->page, $this->parent->OPT_IGNORE_ITEMS );
|
||||
register_setting( $this->page, $this->parent->OPT_SCANNING_INTERVAL, 'sanitize_text_field' );
|
||||
register_setting( $this->page, $this->parent->OPT_SCANNING_TIME, 'sanitize_text_field' );
|
||||
|
||||
$section = $this->page . '_section';
|
||||
|
||||
add_settings_section(
|
||||
$section,
|
||||
null,
|
||||
array( $this, 'introduction' ),
|
||||
$this->page
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_API_TOKEN,
|
||||
__( 'WPScan API Token', 'wpscan' ),
|
||||
array( $this, 'field_api_token' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_SCANNING_INTERVAL,
|
||||
__( 'Automated Scanning', 'wpscan' ),
|
||||
array( $this, 'field_scanning_interval' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_SCANNING_TIME,
|
||||
__( 'Scanning Time', 'wpscan' ),
|
||||
array( $this, 'field_scanning_time' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$this->parent->OPT_IGNORE_ITEMS,
|
||||
__( 'Ignore Items', 'wpscan' ),
|
||||
array( $this, 'field_ignore_items' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
|
||||
if ( $this->parent->is_interval_scanning_disabled() ) {
|
||||
as_unschedule_all_actions( $this->parent->WPSCAN_SCHEDULE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if API Token is set
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function api_token_set() {
|
||||
$api_token = get_option( $this->parent->OPT_API_TOKEN );
|
||||
|
||||
if ( empty( $api_token ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if no API Token is set
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function got_api_token() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( ! $this->api_token_set() && ! strstr( $screen->id, $this->page ) ) {
|
||||
printf(
|
||||
'<div class="%s"><p>%s <a href="%s">%s</a>%s</p></div>',
|
||||
'notice notice-error',
|
||||
__( 'To use WPScan you have to setup your WPScan API Token. Either in the ', 'wpscan' ),
|
||||
admin_url( 'admin.php?page=' . $this->page ),
|
||||
__( 'Settings', 'wpscan' ),
|
||||
__( ' page, or, within the wp-config.php file.', 'wpscan' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add submenu
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function menu() {
|
||||
$title = __( 'Settings', 'wpscan' );
|
||||
|
||||
add_submenu_page(
|
||||
'wpscan',
|
||||
$title,
|
||||
$title,
|
||||
$this->parent->WPSCAN_ROLE,
|
||||
$this->page,
|
||||
array( $this, 'page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the page
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function page() {
|
||||
echo '<div class="wrap">';
|
||||
echo '<h1><img src="' . $this->parent->plugin_url . 'assets/svg/logo.svg" alt="WPScan"></h1>';
|
||||
|
||||
echo '<h2>' . __( 'Settings', 'wpscan' ) . '</h2>';
|
||||
|
||||
echo '<p>' . __( 'The WPScan WordPress security plugin uses our own constantly updated vulnerability database to stay up to date with the latest WordPress core, plugin and theme vulnerabilities. For the WPScan plugin to retrieve the potential vulnerabilities that may affect your site, you first need to configure your API token, that you can get for free from our database\'s website. Alternatively you can also set your API token in the wp-config.php file using the WPSCAN_API_TOKEN constant.', 'wpscan' ) . '</p><br/>';
|
||||
|
||||
settings_errors();
|
||||
|
||||
echo '<form action="options.php" method="post">';
|
||||
settings_fields( $this->page );
|
||||
do_settings_sections( $this->page );
|
||||
|
||||
submit_button();
|
||||
echo '</form>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* API token field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_api_token() {
|
||||
$api_token = esc_attr( get_option( $this->parent->OPT_API_TOKEN ) );
|
||||
|
||||
if ( defined( 'WPSCAN_API_TOKEN' ) ) {
|
||||
$api_token = esc_attr( WPSCAN_API_TOKEN );
|
||||
$disabled = "disabled='true'";
|
||||
} else {
|
||||
$disabled = null;
|
||||
}
|
||||
|
||||
// Field.
|
||||
echo "<input type='text' name='" . $this->parent->OPT_API_TOKEN . "' value='$api_token' class='blur-on-lose-focus regular-text' $disabled>";
|
||||
|
||||
// Messages.
|
||||
echo '<p class="description">';
|
||||
|
||||
if ( defined( 'WPSCAN_API_TOKEN' ) ) {
|
||||
_e( 'Your API Token has been set in a PHP file and been disabled here.', 'wpscan' );
|
||||
echo '<br>';
|
||||
}
|
||||
|
||||
if ( ! empty( $api_token ) ) {
|
||||
echo sprintf(
|
||||
__( 'To regenerate your token, or upgrade your plan, %s.', 'wpscan' ),
|
||||
'<a href="' . WPSCAN_PROFILE_URL . '" target="_blank">' . __( 'check your profile', 'wpscan' ) . '</a>'
|
||||
);
|
||||
} else {
|
||||
echo sprintf(
|
||||
__( '%s to get your free API Token.', 'wpscan' ),
|
||||
'<a href="' . WPSCAN_SIGN_UP_URL . '" target="_blank">' . __( 'Sign up', 'wpscan' ) . '</a>'
|
||||
);
|
||||
}
|
||||
|
||||
echo '</p><br>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Scanning interval field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_scanning_interval() {
|
||||
$opt_name = $this->parent->OPT_SCANNING_INTERVAL;
|
||||
$value = esc_attr( get_option( $opt_name, 'daily' ) );
|
||||
|
||||
$disabled = $this->parent->is_interval_scanning_disabled() ? "disabled='true'" : null;
|
||||
|
||||
$options = array(
|
||||
'daily' => __( 'Daily', 'wpscan' ),
|
||||
'twicedaily' => __( 'Twice daily', 'wpscan' ),
|
||||
'hourly' => __( 'Hourly', 'wpscan' ),
|
||||
);
|
||||
|
||||
echo "<select name='$opt_name' $disabled>";
|
||||
foreach ( $options as $id => $title ) {
|
||||
$selected = selected( $value, $id, false );
|
||||
echo "<option value='$id' $selected>$title</option>";
|
||||
}
|
||||
echo '</select>';
|
||||
|
||||
echo '<p class="description">';
|
||||
|
||||
if ( $this->parent->is_interval_scanning_disabled() ) {
|
||||
_e( 'Automated scanning is currently disabled using the <code>WPSCAN_DISABLE_SCANNING_INTERVAL</code> constant.', 'wpscan' );
|
||||
} else {
|
||||
_e( 'This setting will change the frequency that the WPScan plugin will run an automatic scan. This is useful if you want your report, or notifications, to be updated more frequently. Please note that the more frequent scans are run, the more API requests are consumed.', 'wpscan' );
|
||||
}
|
||||
|
||||
echo '</p><br>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scanning time field.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_scanning_time() {
|
||||
$opt = $this->parent->OPT_SCANNING_TIME;
|
||||
$value = esc_attr( get_option( $opt, date( 'H:i' ) ) );
|
||||
$disabled = $this->parent->is_interval_scanning_disabled() ? "disabled='true'" : null;
|
||||
|
||||
echo "<input type='time' name='$opt' value='$value' $disabled> ";
|
||||
|
||||
if ( ! $this->parent->is_interval_scanning_disabled() ) {
|
||||
echo __( 'Current server time is ', 'wpscan' ) . '<code>' . date( 'H:i' ) . '</code>';
|
||||
}
|
||||
|
||||
echo '<p class="description">';
|
||||
|
||||
if ( $this->parent->is_interval_scanning_disabled() ) {
|
||||
_e( 'Automated scanning is currently disabled using the <code>WPSCAN_DISABLE_SCANNING_INTERVAL</code> constant.', 'wpscan' );
|
||||
} else {
|
||||
_e( 'This setting allows you to set the scanning hour for the <code>Daily</code> option. For the <code>Twice Daily</code> this will be the first scan and the second will be 12 hours later. For the <code>Hourly</code> it will affect the first scan only.', 'wpscan' );
|
||||
}
|
||||
|
||||
echo '</p><br/>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore items field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function field_ignore_items() {
|
||||
$opt = $this->parent->OPT_IGNORE_ITEMS;
|
||||
$value = get_option( $opt, array() );
|
||||
$wp = isset( $value['wordpress'] ) ? 'checked' : null;
|
||||
|
||||
// WordPress.
|
||||
echo "<div class='wpscan-ignore-items-section'>";
|
||||
|
||||
echo "<label><input name='{$opt}[WordPress]' type='checkbox' $wp value='1' > " .
|
||||
__( 'WordPress Core', 'wpscan' ) . '</label>';
|
||||
|
||||
echo '</div>';
|
||||
|
||||
// Plugins list.
|
||||
$this->ignore_items_section( 'plugins', $value );
|
||||
|
||||
// Themes list
|
||||
$this->ignore_items_section( 'themes', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore items section
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function ignore_items_section( $type, $value ) {
|
||||
$opt = $this->parent->OPT_IGNORE_ITEMS;
|
||||
|
||||
$items = 'themes' === $type
|
||||
? wp_get_themes()
|
||||
: get_plugins();
|
||||
|
||||
$title = 'themes' === $type
|
||||
? __( 'Themes', 'wpscan' )
|
||||
: __( 'Plugins', 'wpscan' );
|
||||
|
||||
echo "<div class='wpscan-ignore-items-section'>";
|
||||
|
||||
echo "<h4>$title</h4>";
|
||||
|
||||
foreach ( $items as $name => $details ) {
|
||||
$slug = 'themes' === $type
|
||||
? $this->parent->get_theme_slug( $name, $details )
|
||||
: $this->parent->get_plugin_slug( $name, $details );
|
||||
|
||||
$checked = isset( $value[ $type ][ $slug ] ) ? 'checked' : null;
|
||||
|
||||
echo '<label>' .
|
||||
"<input name='{$opt}[$type][$slug]' type='checkbox' $checked value='1'> " .
|
||||
esc_html( $details['Name'] ) .
|
||||
'</label>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize API token
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_api_token( $value ) {
|
||||
$value = trim( $value );
|
||||
|
||||
// update_account_status() calls the /status API endpoint, verifying the validity of the Token passed via $value and updates the account status if needed.
|
||||
if ( empty( $value ) ) {
|
||||
delete_option( $this->parent->OPT_ACCOUNT_STATUS );
|
||||
} else {
|
||||
$this->parent->classes['account']->update_account_status( $value );
|
||||
}
|
||||
|
||||
$errors = get_option( $this->parent->OPT_ERRORS );
|
||||
|
||||
if ( ! empty( $errors ) ) {
|
||||
foreach ( $errors as $error ) {
|
||||
add_settings_error(
|
||||
$this->page,
|
||||
'api_token',
|
||||
$error
|
||||
);
|
||||
}
|
||||
|
||||
update_option( $this->parent->OPT_ERRORS, array() ); // Clear errors.
|
||||
} else {
|
||||
if ( $this->parent->is_interval_scanning_disabled() ) {
|
||||
as_unschedule_all_actions( $this->parent->WPSCAN_SCHEDULE );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule CRON scanning event
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function schedule_event( $old_value, $value ) {
|
||||
$api_token = get_option( $this->parent->OPT_API_TOKEN );
|
||||
|
||||
if ( ! empty( $api_token ) && $old_value !== $value ) {
|
||||
$interval = esc_attr( get_option( $this->parent->OPT_SCANNING_INTERVAL, 'daily' ) );
|
||||
$time = esc_attr( get_option( $this->parent->OPT_SCANNING_TIME, date( 'H:i' ) . ' +1day' ) );
|
||||
|
||||
as_unschedule_all_actions( $this->parent->WPSCAN_SCHEDULE );
|
||||
|
||||
switch ( $interval ) {
|
||||
case 'daily':
|
||||
$interval = DAY_IN_SECONDS;
|
||||
break;
|
||||
case 'twicedaily':
|
||||
$interval = HOUR_IN_SECONDS * 12;
|
||||
break;
|
||||
case 'hourly':
|
||||
$interval = HOUR_IN_SECONDS;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! $this->parent->is_interval_scanning_disabled() ) {
|
||||
if ( false === as_next_scheduled_action( $this->parent->WPSCAN_SCHEDULE ) ) {
|
||||
as_schedule_recurring_action( $time, $interval, $this->parent->WPSCAN_SCHEDULE );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update ignored items
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function update_ignored_items( $old_value, $value ) {
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
if ( empty( $report ) || $old_value === $value ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( array( 'themes', 'plugins' ) as $type ) {
|
||||
if ( ! isset( $value[ $type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $value[ $type ] as $slug => $checked ) {
|
||||
if ( isset( $report[ $type ][ $slug ] ) ) {
|
||||
// Remove from the report.
|
||||
unset( $report[ $type ][ $slug ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_option( $this->parent->OPT_REPORT, $report, true );
|
||||
}
|
||||
}
|
93
wp-content/plugins/wpscan/app/SiteHealth.php
Normal file
93
wp-content/plugins/wpscan/app/SiteHealth.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* SiteHealth.
|
||||
*
|
||||
* Displays vulnerabilities in WordPress site health page.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class SiteHealth {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
|
||||
add_filter( 'site_status_tests', array( $this, 'add_site_health_tests' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add site-health page tests.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function add_site_health_tests( $tests ) {
|
||||
$tests['direct']['wpscan_check'] = array(
|
||||
'label' => __( 'WPScan Vulnerabilities Check' ),
|
||||
'test' => array( $this, 'site_health_tests' ),
|
||||
);
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do site-health page tests
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function site_health_tests() {
|
||||
$report = $this->parent->get_report();
|
||||
$total = $this->parent->get_total_not_ignored();
|
||||
$vulns = $this->parent->classes['report']->get_all_vulnerabilities();
|
||||
|
||||
/**
|
||||
* Default, no vulnerabilities found
|
||||
*/
|
||||
$result = array(
|
||||
'label' => __( 'No known vulnerabilities found', 'wpscan' ),
|
||||
'status' => 'good',
|
||||
'badge' => array(
|
||||
'label' => __( 'Security', 'wpscan' ),
|
||||
'color' => 'gray',
|
||||
),
|
||||
'description' => sprintf(
|
||||
'<p>%s</p>',
|
||||
__( 'Vulnerabilities can be exploited by hackers and cause harm to your website.', 'wpscan' )
|
||||
),
|
||||
'actions' => '',
|
||||
'test' => 'wpscan_check',
|
||||
);
|
||||
|
||||
/**
|
||||
* If vulnerabilities found.
|
||||
*/
|
||||
if ( ! empty($report) && $total > 0 ) {
|
||||
$result['status'] = 'critical';
|
||||
$result['label'] = sprintf( _n( 'Your site is affected by %d security vulnerability', 'Your site is affected by %d security vulnerabilities', $total, 'wpscan' ), $total );
|
||||
$result['description'] = 'WPScan detected the following security vulnerabilities in your site:';
|
||||
|
||||
foreach ( $vulns as $vuln ) {
|
||||
$result['description'] .= '<p>';
|
||||
$result['description'] .= "<span class='dashicons dashicons-warning' style='color: crimson;'></span>  ";
|
||||
$result['description'] .= wp_kses( $vuln, array( 'a' => array( 'href' => array() ) ) ); // Only allow a href HTML tags.
|
||||
$result['description'] .= '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
217
wp-content/plugins/wpscan/app/Summary.php
Normal file
217
wp-content/plugins/wpscan/app/Summary.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Summary.
|
||||
*
|
||||
* Displays the Summary box.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Summary {
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
|
||||
add_action( 'admin_init', array( $this, 'add_meta_box_summary' ) );
|
||||
add_action( 'wp_ajax_wpscan_check_now', array( $this, 'ajax_check_now' ) );
|
||||
add_action( 'wp_ajax_wpscan_security_check_now', array( $this, 'ajax_security_check_now' ) );
|
||||
add_action( 'wp_ajax_' . $this->parent->WPSCAN_TRANSIENT_CRON, array( $this, 'ajax_doing_cron' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta box
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function add_meta_box_summary() {
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
add_meta_box(
|
||||
'wpscan-metabox-summary',
|
||||
__( 'Summary', 'wpscan' ),
|
||||
array( $this, 'do_meta_box_summary' ),
|
||||
'wpscan',
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box
|
||||
*
|
||||
* @return string
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function do_meta_box_summary() {
|
||||
$report = $this->parent->get_report();
|
||||
$errors = get_option( $this->parent->OPT_ERRORS );
|
||||
$total = $this->parent->get_total_not_ignored();
|
||||
?>
|
||||
|
||||
<?php
|
||||
// Check if we have run a scan yet.
|
||||
if ( ! empty( $this->parent->get_report() ) ) {
|
||||
?>
|
||||
|
||||
<?php
|
||||
if ( ! empty( $errors ) ) {
|
||||
foreach ( $errors as $err ) {
|
||||
// $err should not contain user input. If you like to add an esc_html() here, be sure to update the error text that use HTML
|
||||
echo '<p class="wpscan-summary-res is-red"><span class="dashicons dashicons-megaphone"></span> <strong>' . $err . '</strong></p>';
|
||||
}
|
||||
} elseif ( empty( $this->parent->get_report() ) ) { // No scan run yet.
|
||||
echo '<p class="wpscan-summary-res is-red"><span class="dashicons dashicons-megaphone"></span> <strong>' . __( 'No scan run yet!', 'wpscan' ) . '</strong></p>';
|
||||
} elseif ( empty( $errors ) && 0 === $total ) {
|
||||
echo '<p class="wpscan-summary-res is-green"><span class="dashicons dashicons-awards"></span> <strong>' . __( 'No known vulnerabilities found', 'wpscan' ) . '</strong></p>';
|
||||
} elseif ( ! get_option( $this->parent->OPT_API_TOKEN ) ) {
|
||||
echo '<p class="wpscan-summary-res is-red"><span class="dashicons dashicons-megaphone"></span> <strong>' . __( 'You need to add a WPScan API Token to the settings page', 'wpscan' ) . '</strong></p>';
|
||||
} else {
|
||||
echo '<p class="wpscan-summary-res is-red"><span class="dashicons dashicons-megaphone"></span> <strong>' . __( 'Some vulnerabilities were found', 'wpscan' ) . '</strong></p>';
|
||||
}
|
||||
?>
|
||||
|
||||
<p>
|
||||
<?php _e( 'The last full scan was run on: ', 'wpscan' ); ?>
|
||||
</p>
|
||||
<p>
|
||||
<span class="dashicons dashicons-calendar-alt"></span>
|
||||
|
||||
<strong>
|
||||
<?php
|
||||
if ( array_key_exists( 'cache', $report ) ) {
|
||||
echo date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $report['cache'] );
|
||||
} else {
|
||||
echo _e( 'No full scan yet', 'wpscan' );
|
||||
}
|
||||
?>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<?php if ( false !== as_next_scheduled_action( $this->parent->WPSCAN_SCHEDULE ) ) { ?>
|
||||
<p>
|
||||
<?php _e( 'The next scan will automatically be run on ', 'wpscan' ); ?>
|
||||
<?php echo date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), as_next_scheduled_action( $this->parent->WPSCAN_SCHEDULE ) ); ?>
|
||||
</p>
|
||||
<?php } ?>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<p class="description">
|
||||
<?php
|
||||
if ( get_option( $this->parent->OPT_API_TOKEN ) ) {
|
||||
_e( 'Click the Run All button to run a full vulnerability scan against your WordPress website.', 'wpscan' );
|
||||
} else {
|
||||
_e( 'Add your API token to the settings page to be able to run a full scan.', 'wpscan' );
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php if ( get_option( $this->parent->OPT_API_TOKEN ) ) : ?>
|
||||
<p class="check-now">
|
||||
<?php
|
||||
$spinner_display = '';
|
||||
$button_disabled = '';
|
||||
if ( false !== as_next_scheduled_action( $this->parent->WPSCAN_RUN_ALL ) ) {
|
||||
$spinner_display = ' style="visibility: visible;"';
|
||||
$button_disabled = 'disabled';
|
||||
}
|
||||
?>
|
||||
<span class="spinner"<?php echo $spinner_display; ?>></span>
|
||||
<button type="button" class="button button-primary"<?php echo $button_disabled; ?>><?php _e( 'Run All', 'wpscan' ); ?></button>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax check now
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function ajax_check_now() {
|
||||
check_ajax_referer( 'wpscan' );
|
||||
|
||||
if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
|
||||
wp_redirect( home_url() );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( false === as_next_scheduled_action( $this->parent->WPSCAN_RUN_ALL ) ) {
|
||||
as_schedule_single_action( strtotime( 'now' ), $this->parent->WPSCAN_RUN_ALL );
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax scurity check now
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function ajax_security_check_now() {
|
||||
check_ajax_referer( 'wpscan' );
|
||||
|
||||
if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
|
||||
wp_redirect( home_url() );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$items_inline = get_option( $this->parent->WPSCAN_RUN_SECURITY );
|
||||
|
||||
$plugins = array();
|
||||
foreach ( $this->parent->classes['checks/system']->checks as $id => $data ) {
|
||||
$plugins[ $id ] = array(
|
||||
'status' => $this->parent->classes['report']->get_status( 'security-checks', $id ),
|
||||
'vulnerabilities' => $this->parent->classes['checks/system']->get_check_vulnerabilities( $data['instance'] ),
|
||||
'security-check-actions' => $this->parent->classes['checks/system']->get_list_actions( $data['instance'] ),
|
||||
);
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'inline' => $items_inline,
|
||||
'plugins' => $plugins,
|
||||
);
|
||||
|
||||
wp_die( wp_json_encode( $response ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax to check when the cron task has finished
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public function ajax_doing_cron() {
|
||||
check_ajax_referer( 'wpscan' );
|
||||
|
||||
if ( ! current_user_can( $this->parent->WPSCAN_ROLE ) ) {
|
||||
wp_redirect( home_url() );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// echo get_transient( $this->parent->WPSCAN_TRANSIENT_CRON ) ? 'YES' : 'NO';
|
||||
echo false !== as_next_scheduled_action( $this->parent->WPSCAN_RUN_ALL ) ? 'YES' : 'NO';
|
||||
|
||||
wp_die();
|
||||
}
|
||||
}
|
189
wp-content/plugins/wpscan/app/ignoreVulnerabilities.php
Normal file
189
wp-content/plugins/wpscan/app/ignoreVulnerabilities.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace WPScan;
|
||||
|
||||
// Exit if accessed directly.
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* IgnoreVulnerabilities.
|
||||
*
|
||||
* Used for the ignore vulnerabilities logic.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ignoreVulnerabilities {
|
||||
// Page slug.
|
||||
private $page;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param object $parent parent.
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $parent ) {
|
||||
$this->parent = $parent;
|
||||
$this->page = 'wpscan_ignore_vulnerabilities';
|
||||
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'admin_init', array( $this, 'add_meta_box_ignore_vulnerabilities' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore vulnerabilities option
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function admin_init() {
|
||||
$total = $this->parent->get_total();
|
||||
|
||||
register_setting( $this->page, $this->parent->OPT_IGNORED, array( $this, 'sanitize_ignored' ) );
|
||||
|
||||
$section = $this->page . '_section';
|
||||
|
||||
add_settings_section(
|
||||
$section,
|
||||
null,
|
||||
array( $this, 'introduction' ),
|
||||
$this->page
|
||||
);
|
||||
|
||||
if ( $total > 0 ) {
|
||||
add_settings_field(
|
||||
$this->parent->OPT_IGNORED,
|
||||
null,
|
||||
array( $this, 'field_ignored' ),
|
||||
$this->page,
|
||||
$section
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function add_meta_box_ignore_vulnerabilities() {
|
||||
add_meta_box(
|
||||
'wpscan-metabox-ignore-vulnerabilities',
|
||||
__( 'Ignore Vulnerabilities', 'wpscan' ),
|
||||
array( $this, 'do_meta_box_ignore_vulnerabilities' ),
|
||||
'wpscan',
|
||||
'side',
|
||||
'low'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function do_meta_box_ignore_vulnerabilities() {
|
||||
echo '<form action="options.php" method="post">';
|
||||
|
||||
settings_fields( $this->page );
|
||||
|
||||
do_settings_sections( $this->page );
|
||||
|
||||
submit_button();
|
||||
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Introduction
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function introduction() { }
|
||||
|
||||
/**
|
||||
* Ignored field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function field_ignored() {
|
||||
$this->list_vulnerabilities_to_ignore( 'wordpress', get_bloginfo( 'version' ) );
|
||||
|
||||
foreach ( get_plugins() as $name => $details ) {
|
||||
$this->list_vulnerabilities_to_ignore( 'plugins', $this->parent->get_plugin_slug( $name, $details ) );
|
||||
}
|
||||
|
||||
foreach ( wp_get_themes() as $name => $details ) {
|
||||
$this->list_vulnerabilities_to_ignore( 'themes', $this->parent->get_theme_slug( $name, $details ) );
|
||||
}
|
||||
|
||||
foreach ( $this->parent->classes['checks/system']->checks as $id => $data ) {
|
||||
$this->list_vulnerabilities_to_ignore( 'security-checks', $id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize ignored
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $value value.
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_ignored( $value ) {
|
||||
if ( empty( $value ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of vulnerabilities
|
||||
*
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param string $type - Type of report: wordpress, plugins, themes.
|
||||
* @param string $name - key name of the element.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function list_vulnerabilities_to_ignore( $type, $name ) {
|
||||
$report = $this->parent->get_report();
|
||||
|
||||
if ( isset( $report[ $type ] ) && isset( $report[ $type ][ $name ] ) ) {
|
||||
$report = $report[ $type ][ $name ];
|
||||
}
|
||||
|
||||
if ( ! isset( $report['vulnerabilities'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ignored = $this->parent->get_ignored_vulnerabilities();
|
||||
|
||||
foreach ( $report['vulnerabilities'] as $item ) {
|
||||
$id = 'security-checks' === $type ? $item['id'] : $item->id;
|
||||
$title = 'security-checks' === $type ? $item['title'] : $this->parent->get_sanitized_vulnerability_title( $item );
|
||||
|
||||
echo sprintf(
|
||||
'<label><input type="checkbox" name="%s[]" value="%s" %s> %s</label><br>',
|
||||
esc_attr( $this->parent->OPT_IGNORED ),
|
||||
esc_attr( $id ),
|
||||
esc_html( in_array( $id, $ignored, true ) ? 'checked="checked"' : null ),
|
||||
wp_kses( $title, array( 'a' => array( 'href' => array() ) ) ) // Only allow a href HTML tags.
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user