installed plugin Jetpack Protect
version 1.0.2
This commit is contained in:
260
wp-content/plugins/jetpack-protect/src/class-jetpack-protect.php
Normal file
260
wp-content/plugins/jetpack-protect/src/class-jetpack-protect.php
Normal file
@ -0,0 +1,260 @@
|
||||
<?php
|
||||
/**
|
||||
* Primary class file for the Jetpack Protect plugin.
|
||||
*
|
||||
* @package automattic/jetpack-protect-plugin
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use Automattic\Jetpack\Admin_UI\Admin_Menu;
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
|
||||
use Automattic\Jetpack\My_Jetpack\Initializer as My_Jetpack_Initializer;
|
||||
use Automattic\Jetpack\My_Jetpack\Products as My_Jetpack_Products;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Automattic\Jetpack\Protect\Site_Health;
|
||||
use Automattic\Jetpack\Protect\Status as Protect_Status;
|
||||
use Automattic\Jetpack\Sync\Functions as Sync_Functions;
|
||||
use Automattic\Jetpack\Sync\Sender;
|
||||
/**
|
||||
* Class Jetpack_Protect
|
||||
*/
|
||||
class Jetpack_Protect {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
// Init Jetpack packages and ConnectionUI.
|
||||
add_action(
|
||||
'plugins_loaded',
|
||||
function () {
|
||||
$config = new Automattic\Jetpack\Config();
|
||||
// Connection package.
|
||||
$config->ensure(
|
||||
'connection',
|
||||
array(
|
||||
'slug' => JETPACK_PROTECT_SLUG,
|
||||
'name' => JETPACK_PROTECT_NAME,
|
||||
'url_info' => JETPACK_PROTECT_URI,
|
||||
)
|
||||
);
|
||||
// Sync package.
|
||||
$config->ensure(
|
||||
'sync',
|
||||
array(
|
||||
'jetpack_sync_modules' => array(
|
||||
'Automattic\\Jetpack\\Sync\\Modules\\Options',
|
||||
'Automattic\\Jetpack\\Sync\\Modules\\Callables',
|
||||
),
|
||||
'jetpack_sync_callable_whitelist' => array(
|
||||
'get_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins' ),
|
||||
'get_themes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_themes' ),
|
||||
'wp_version' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'wp_version' ),
|
||||
),
|
||||
'jetpack_sync_options_contentless' => array(),
|
||||
'jetpack_sync_options_whitelist' => array(
|
||||
'active_plugins',
|
||||
'stylesheet',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Identity crisis package.
|
||||
$config->ensure( 'identity_crisis' );
|
||||
},
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
// Set up the REST authentication hooks.
|
||||
Connection_Rest_Authentication::init();
|
||||
|
||||
$total_vuls = Protect_Status::get_total_vulnerabilities();
|
||||
$menu_label = _x( 'Protect', 'The Jetpack Protect product name, without the Jetpack prefix', 'jetpack-protect' );
|
||||
if ( $total_vuls ) {
|
||||
$menu_label .= sprintf( ' <span class="update-plugins">%d</span>', $total_vuls );
|
||||
}
|
||||
|
||||
$page_suffix = Admin_Menu::add_menu(
|
||||
__( 'Jetpack Protect', 'jetpack-protect' ),
|
||||
$menu_label,
|
||||
'manage_options',
|
||||
'jetpack-protect',
|
||||
array( $this, 'plugin_settings_page' ),
|
||||
99
|
||||
);
|
||||
add_action( 'load-' . $page_suffix, array( $this, 'admin_init' ) );
|
||||
|
||||
add_action( 'admin_bar_menu', array( $this, 'admin_bar' ), 65 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
|
||||
// Add custom WP REST API endoints.
|
||||
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_endpoints' ) );
|
||||
|
||||
My_Jetpack_Initializer::init();
|
||||
Site_Health::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the admin resources.
|
||||
*/
|
||||
public function admin_init() {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the wp-admin styles (used outside the React app)
|
||||
*/
|
||||
public function enqueue_admin_styles() {
|
||||
wp_enqueue_style( 'jetpack-protect-wpadmin', JETPACK_PROTECT_BASE_PLUGIN_URL . '/assets/jetpack-protect.css', array(), JETPACK_PROTECT_VERSION );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue plugin admin scripts and styles.
|
||||
*/
|
||||
public function enqueue_admin_scripts() {
|
||||
|
||||
Assets::register_script(
|
||||
'jetpack-protect',
|
||||
'build/index.js',
|
||||
JETPACK_PROTECT_ROOT_FILE,
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'textdomain' => 'jetpack-protect',
|
||||
)
|
||||
);
|
||||
Assets::enqueue_script( 'jetpack-protect' );
|
||||
// Required for Analytics.
|
||||
wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
|
||||
// Initial JS state including JP Connection data.
|
||||
wp_add_inline_script( 'jetpack-protect', Connection_Initial_State::render(), 'before' );
|
||||
wp_add_inline_script( 'jetpack-protect', $this->render_initial_state(), 'before' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the initial state into a JavaScript variable.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render_initial_state() {
|
||||
return 'var jetpackProtectInitialState=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->initial_state() ) ) . '"));';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial state data for hydrating the React UI.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function initial_state() {
|
||||
global $wp_version;
|
||||
return array(
|
||||
'apiRoot' => esc_url_raw( rest_url() ),
|
||||
'apiNonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'registrationNonce' => wp_create_nonce( 'jetpack-registration-nonce' ),
|
||||
'status' => Protect_Status::get_status(),
|
||||
'installedPlugins' => Plugins_Installer::get_plugins(),
|
||||
'installedThemes' => Sync_Functions::get_themes(),
|
||||
'wpVersion' => $wp_version,
|
||||
'adminUrl' => admin_url( 'admin.php?page=jetpack-protect' ),
|
||||
'securityBundle' => My_Jetpack_Products::get_product( 'security' ),
|
||||
'productData' => My_Jetpack_Products::get_product( 'protect' ),
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Main plugin settings page.
|
||||
*/
|
||||
public function plugin_settings_page() {
|
||||
?>
|
||||
<div id="jetpack-protect-root"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes plugin from the connection manager
|
||||
* If it's the last plugin using the connection, the site will be disconnected.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function plugin_deactivation() {
|
||||
|
||||
// Clear Sync data.
|
||||
Sender::get_instance()->uninstall();
|
||||
|
||||
$manager = new Connection_Manager( 'jetpack-protect' );
|
||||
$manager->remove_connection();
|
||||
|
||||
Protect_Status::delete_option();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shortcut on Admin Bar to show the total of vulnerabilities found.
|
||||
*
|
||||
* @param object $wp_admin_bar The Admin Bar object.
|
||||
* @return void
|
||||
*/
|
||||
public function admin_bar( $wp_admin_bar ) {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$total = Protect_Status::get_total_vulnerabilities();
|
||||
|
||||
if ( $total > 0 ) {
|
||||
$args = array(
|
||||
'id' => 'jetpack-protect',
|
||||
'title' => '<span class="ab-icon jp-protect-icon"></span><span class="ab-label">' . $total . '</span>',
|
||||
'href' => admin_url( 'admin.php?page=jetpack-protect' ),
|
||||
'meta' => array(
|
||||
// translators: %d is the number of vulnerabilities found.
|
||||
'title' => sprintf( _n( '%d vulnerability found by Jetpack Protect', '%d vulnerabilities found by Jetpack Protect', $total, 'jetpack-protect' ), $total ),
|
||||
),
|
||||
);
|
||||
|
||||
$wp_admin_bar->add_node( $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the REST API routes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_rest_endpoints() {
|
||||
register_rest_route(
|
||||
'jetpack-protect/v1',
|
||||
'status',
|
||||
array(
|
||||
'methods' => \WP_REST_Server::READABLE,
|
||||
'callback' => __CLASS__ . '::api_get_status',
|
||||
'permission_callback' => function () {
|
||||
return current_user_can( 'manage_options' );
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Protect Status for the API endpoint
|
||||
*
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public static function api_get_status() {
|
||||
$status = Protect_Status::get_status();
|
||||
return rest_ensure_response( $status, 200 );
|
||||
}
|
||||
}
|
111
wp-content/plugins/jetpack-protect/src/class-site-health.php
Normal file
111
wp-content/plugins/jetpack-protect/src/class-site-health.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Check in the Site Health admin page
|
||||
*
|
||||
* @package automattic/jetpack-protect-plugin
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect;
|
||||
|
||||
/**
|
||||
* Site_Health.
|
||||
*
|
||||
* Displays vulnerabilities in WordPress site health page.
|
||||
*/
|
||||
class Site_Health {
|
||||
|
||||
/**
|
||||
* Initialize hooks
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
if ( ! has_filter( 'site_status_tests', array( __CLASS__, 'add_check' ) ) ) {
|
||||
add_filter( 'site_status_tests', array( __CLASS__, 'add_check' ), 99 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add site-health page tests.
|
||||
*
|
||||
* @param array $checks Core checks.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function add_check( $checks ) {
|
||||
$checks['direct']['jetpack_protect_checks'] = array(
|
||||
'label' => __( 'Jetpack Protect checks', 'jetpack-protect' ),
|
||||
'test' => array( __CLASS__, 'do_checks' ),
|
||||
);
|
||||
|
||||
return $checks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do site-health page checks
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public static function do_checks() {
|
||||
$total_vuls = Status::get_total_vulnerabilities();
|
||||
$vulns = Status::get_all_vulnerabilities();
|
||||
$vulns = array_map(
|
||||
function ( $v ) {
|
||||
return $v->title;
|
||||
},
|
||||
$vulns
|
||||
);
|
||||
|
||||
/**
|
||||
* Default, no vulnerabilities found
|
||||
*/
|
||||
$result = array(
|
||||
'label' => __( 'No known vulnerabilities found', 'jetpack-protect' ),
|
||||
'status' => 'good',
|
||||
'badge' => array(
|
||||
'label' => __( 'Security', 'jetpack-protect' ),
|
||||
'color' => 'gray',
|
||||
),
|
||||
'description' => sprintf(
|
||||
'<p>%s</p>',
|
||||
__( 'Jetpack Protect did not find any known vulnerabilities in your site. Vulnerabilities can be exploited by hackers and cause harm to your website.', 'jetpack-protect' )
|
||||
),
|
||||
'actions' => '',
|
||||
'test' => 'jetpack_protect_checks',
|
||||
);
|
||||
|
||||
/**
|
||||
* If vulnerabilities found.
|
||||
*/
|
||||
if ( $total_vuls ) {
|
||||
$result['status'] = 'critical';
|
||||
/* translators: $d is the number of vulnerabilities found. */
|
||||
$result['label'] = sprintf( _n( 'Your site is affected by %d security vulnerability', 'Your site is affected by %d security vulnerabilities', $total_vuls, 'jetpack-protect' ), $total_vuls );
|
||||
$result['description'] = __( 'Jetpack Protect detected the following security vulnerabilities in your site:', 'jetpack-protect' );
|
||||
|
||||
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>';
|
||||
}
|
||||
$result['description'] .= '<p>';
|
||||
$result['description'] .= sprintf(
|
||||
wp_kses(
|
||||
/* translators: Link to Jetpack Protect. */
|
||||
__( 'See <a href="%s">Protect overview page</a> for more information.', 'jetpack-protect' ),
|
||||
array(
|
||||
'a' => array( 'href' => array() ),
|
||||
)
|
||||
),
|
||||
esc_url( admin_url( 'admin.php?page=jetpack-protect' ) )
|
||||
);
|
||||
$result['description'] .= '</p>';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
409
wp-content/plugins/jetpack-protect/src/class-status.php
Normal file
409
wp-content/plugins/jetpack-protect/src/class-status.php
Normal file
@ -0,0 +1,409 @@
|
||||
<?php
|
||||
/**
|
||||
* Class to handle the Status of Jetpack Protect
|
||||
*
|
||||
* @package automattic/jetpack-protect-plugin
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Protect;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Plugins_Installer;
|
||||
use Automattic\Jetpack\Sync\Functions as Sync_Functions;
|
||||
use Jetpack_Options;
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Class that handles fetching and caching the Status of vulnerabilities check from the WPCOM servers
|
||||
*/
|
||||
class Status {
|
||||
|
||||
/**
|
||||
* WPCOM endpoint
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REST_API_BASE = '/sites/%d/jetpack-protect-status';
|
||||
|
||||
/**
|
||||
* Name of the option where status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = 'jetpack_protect_status';
|
||||
|
||||
/**
|
||||
* Name of the option where the timestamp of the status is stored
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_TIMESTAMP_NAME = 'jetpack_protect_status_time';
|
||||
|
||||
/**
|
||||
* Time in seconds that the cache should last
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const OPTION_EXPIRES_AFTER = 3600; // 1 hour.
|
||||
|
||||
/**
|
||||
* Time in seconds that the cache for the initial empty response should last
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const INITIAL_OPTION_EXPIRES_AFTER = 1 * MINUTE_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* Memoization for the current status
|
||||
*
|
||||
* @var null|array
|
||||
*/
|
||||
public static $status = null;
|
||||
|
||||
/**
|
||||
* Gets the current status of the Jetpack Protect checks
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_status() {
|
||||
if ( self::$status !== null ) {
|
||||
return self::$status;
|
||||
}
|
||||
|
||||
if ( ! self::should_use_cache() || self::is_cache_expired() ) {
|
||||
$status = self::fetch_from_server();
|
||||
} else {
|
||||
$status = self::get_from_options();
|
||||
}
|
||||
|
||||
if ( is_wp_error( $status ) ) {
|
||||
$status = array(
|
||||
'error' => true,
|
||||
'error_code' => $status->get_error_code(),
|
||||
'error_message' => $status->get_error_message(),
|
||||
);
|
||||
} else {
|
||||
$status = self::normalize_report_data( $status );
|
||||
}
|
||||
|
||||
self::$status = $status;
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current status to see if there are any vulnerabilities found
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function has_vulnerabilities() {
|
||||
return 0 < self::get_total_vulnerabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of vulnerabilities found
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function get_total_vulnerabilities() {
|
||||
$status = self::get_status();
|
||||
return isset( $status->num_vulnerabilities ) && is_int( $status->num_vulnerabilities ) ? $status->num_vulnerabilities : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all vulnerabilities combined
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_all_vulnerabilities() {
|
||||
return array_merge(
|
||||
self::get_wordpress_vulnerabilities(),
|
||||
self::get_themes_vulnerabilities(),
|
||||
self::get_plugins_vulnerabilities()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vulnerabilities found for WordPress core
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_wordpress_vulnerabilities() {
|
||||
return self::get_vulnerabilities( 'core' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vulnerabilities found for themes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_themes_vulnerabilities() {
|
||||
return self::get_vulnerabilities( 'themes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get vulnerabilities found for plugins
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugins_vulnerabilities() {
|
||||
return self::get_vulnerabilities( 'plugins' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vulnerabilities for one type of extension or core
|
||||
*
|
||||
* @param string $type What vulnerabilities you want to get. Possible values are 'core', 'themes' and 'plugins'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_vulnerabilities( $type ) {
|
||||
$status = self::get_status();
|
||||
if ( 'core' === $type ) {
|
||||
return isset( $status->$type ) && ! empty( $status->$type->vulnerabilities ) ? $status->$type->vulnerabilities : array();
|
||||
}
|
||||
|
||||
$vuls = array();
|
||||
if ( isset( $status->$type ) ) {
|
||||
foreach ( (array) $status->$type as $item ) {
|
||||
if ( ! empty( $item->vulnerabilities ) ) {
|
||||
$vuls = array_merge( $vuls, $item->vulnerabilities );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $vuls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current cached status is expired and should be renewed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_cache_expired() {
|
||||
$option_timestamp = get_option( self::OPTION_TIMESTAMP_NAME );
|
||||
|
||||
if ( ! $option_timestamp ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return time() > (int) $option_timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we should consider the stored cache or bypass it
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function should_use_cache() {
|
||||
return defined( 'JETPACK_PROTECT_DEV__BYPASS_CACHE' ) && JETPACK_PROTECT_DEV__BYPASS_CACHE ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the WPCOM API endpoint
|
||||
*
|
||||
* @return WP_Error|string
|
||||
*/
|
||||
public static function get_api_url() {
|
||||
$blog_id = Jetpack_Options::get_option( 'id' );
|
||||
$is_connected = ( new Connection_Manager() )->is_connected();
|
||||
|
||||
if ( ! $blog_id || ! $is_connected ) {
|
||||
return new WP_Error( 'site_not_connected' );
|
||||
}
|
||||
|
||||
$api_url = sprintf( self::REST_API_BASE, $blog_id );
|
||||
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the status from WPCOM servers
|
||||
*
|
||||
* @return WP_Error|array
|
||||
*/
|
||||
public static function fetch_from_server() {
|
||||
$api_url = self::get_api_url();
|
||||
if ( is_wp_error( $api_url ) ) {
|
||||
return $api_url;
|
||||
}
|
||||
|
||||
$response = Client::wpcom_json_api_request_as_blog(
|
||||
self::get_api_url(),
|
||||
'2',
|
||||
array( 'method' => 'GET' ),
|
||||
null,
|
||||
'wpcom'
|
||||
);
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( is_wp_error( $response ) || 200 !== $response_code || empty( $response['body'] ) ) {
|
||||
return new WP_Error( 'failed_fetching_status', 'Failed to fetch Protect Status data from server', array( 'status' => $response_code ) );
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ) );
|
||||
self::update_option( $body );
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current cached status
|
||||
*
|
||||
* @return bool|array False if value is not found. Array with values if cache is found.
|
||||
*/
|
||||
public static function get_from_options() {
|
||||
return get_option( self::OPTION_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated the cached status and its timestamp
|
||||
*
|
||||
* @param array $status The new status to be cached.
|
||||
* @return void
|
||||
*/
|
||||
public static function update_option( $status ) {
|
||||
// TODO: Sanitize $status.
|
||||
update_option( self::OPTION_NAME, $status );
|
||||
$end_date = self::get_cache_end_date_by_status( $status );
|
||||
update_option( self::OPTION_TIMESTAMP_NAME, $end_date );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp the cache should expire depending on the current status
|
||||
*
|
||||
* Initial empty status, which are returned before the first check was performed, should be cache for less time
|
||||
*
|
||||
* @param object $status The response from the server being cached.
|
||||
* @return int The timestamp when the cache should expire.
|
||||
*/
|
||||
public static function get_cache_end_date_by_status( $status ) {
|
||||
if ( ! is_object( $status ) || empty( $status->last_checked ) ) {
|
||||
return time() + self::INITIAL_OPTION_EXPIRES_AFTER;
|
||||
}
|
||||
return time() + self::OPTION_EXPIRES_AFTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the cached status and its timestamp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function delete_option() {
|
||||
delete_option( self::OPTION_NAME );
|
||||
delete_option( self::OPTION_TIMESTAMP_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the report data for the UI
|
||||
*
|
||||
* @param string $report_data The report status report response.
|
||||
* @return object The normalized report data.
|
||||
*/
|
||||
private static function normalize_report_data( $report_data ) {
|
||||
$installed_plugins = Plugins_Installer::get_plugins();
|
||||
$last_report_plugins = isset( $report_data->plugins ) ? $report_data->plugins : new \stdClass();
|
||||
$report_data->plugins = self::merge_installed_and_checked_lists( $installed_plugins, $last_report_plugins, array( 'type' => 'plugin' ) );
|
||||
|
||||
$installed_themes = Sync_Functions::get_themes();
|
||||
$last_report_themes = isset( $report_data->themes ) ? $report_data->themes : new \stdClass();
|
||||
$report_data->themes = self::merge_installed_and_checked_lists( $installed_themes, $last_report_themes, array( 'type' => 'theme' ) );
|
||||
|
||||
$report_data->core = self::normalize_core_information( isset( $report_data->core ) ? $report_data->core : new \stdClass() );
|
||||
|
||||
$all_items = array_merge( $report_data->plugins, $report_data->themes, array( $report_data->core ) );
|
||||
$unchecked_items = array_filter(
|
||||
$all_items,
|
||||
function ( $item ) {
|
||||
return ! isset( $item->checked ) || ! $item->checked;
|
||||
}
|
||||
);
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
$report_data->hasUncheckedItems = ! empty( $unchecked_items );
|
||||
|
||||
return $report_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the list of installed extensions with the list of extensions that were checked for known vulnerabilities and return a normalized list to be used in the UI
|
||||
*
|
||||
* @param array $installed The list of installed extensions, where each attribute key is the extension slug.
|
||||
* @param array $checked The list of checked extensions.
|
||||
* @param array $append Additional data to append to each result in the list.
|
||||
* @return array Normalized list of extensions.
|
||||
*/
|
||||
private static function merge_installed_and_checked_lists( $installed, $checked, $append ) {
|
||||
$new_list = array();
|
||||
foreach ( $installed as $slug => $item ) {
|
||||
if ( isset( $checked->{ $slug } ) && $checked->{ $slug }->version === $installed[ $slug ]['Version'] ) {
|
||||
$new_list[] = (object) array_merge(
|
||||
array(
|
||||
'name' => $installed[ $slug ]['Name'],
|
||||
'version' => $checked->{ $slug }->version,
|
||||
'slug' => $slug,
|
||||
'vulnerabilities' => $checked->{ $slug }->vulnerabilities,
|
||||
'checked' => true,
|
||||
),
|
||||
$append
|
||||
);
|
||||
} else {
|
||||
$new_list[] = (object) array_merge(
|
||||
array(
|
||||
'name' => $installed[ $slug ]['Name'],
|
||||
'version' => $installed[ $slug ]['Version'],
|
||||
'slug' => $slug,
|
||||
'vulnerabilities' => array(),
|
||||
'checked' => false,
|
||||
),
|
||||
$append
|
||||
);
|
||||
}
|
||||
}
|
||||
usort(
|
||||
$new_list,
|
||||
function ( $a, $b ) {
|
||||
// sort primarily based on the presence of vulnerabilities
|
||||
if ( ! empty( $a->vulnerabilities ) && empty( $b->vulnerabilities ) ) {
|
||||
return -1;
|
||||
}
|
||||
if ( empty( $a->vulnerabilities ) && ! empty( $b->vulnerabilities ) ) {
|
||||
return 1;
|
||||
}
|
||||
// sort secondarily on whether the item has been checked
|
||||
if ( $a->checked && ! $b->checked ) {
|
||||
return 1;
|
||||
}
|
||||
if ( ! $a->checked && $b->checked ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
return $new_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the WordPress version that was checked matches the current installed version.
|
||||
*
|
||||
* @param object $core_check The object returned by Protect wpcom endpoint.
|
||||
* @return object The object representing the current status of core checks.
|
||||
*/
|
||||
private static function normalize_core_information( $core_check ) {
|
||||
global $wp_version;
|
||||
|
||||
$core = new \stdClass();
|
||||
if ( isset( $core_check->version ) && $core_check->version === $wp_version ) {
|
||||
$core = $core_check;
|
||||
$core->name = 'WordPress';
|
||||
$core->type = 'core';
|
||||
} else {
|
||||
$core->version = $wp_version;
|
||||
$core->vulnerabilities = array();
|
||||
$core->checked = false;
|
||||
$core->name = 'WordPress';
|
||||
$core->type = 'core';
|
||||
}
|
||||
return $core;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user