Submit
Path:
~
/
home
/
b
/
f
/
x
/
bfxleof
/
www
/
wp-content
/
plugins
/
secupress
/
inc
/
classes
/
scanners
/
File Content:
class-secupress-scan.php
<?php defined( 'ABSPATH' ) or die( 'Something went wrong.' ); /** * Base scan interface. * * @package SecuPress * @since 1.0 */ interface SecuPress_Scan_Interface { /** * Get messages. * * @since 1.0 * * @param (int) $message_id A message ID. */ public static function get_messages( $message_id = null ); /** * Scan for flaw(s). * * @since 1.0 */ public function scan(); /** * Try to fix the flaw(s). * * @since 1.0 */ public function fix(); } /** * Base scan abstract class. * * @package SecuPress * @since 1.0 */ abstract class SecuPress_Scan extends SecuPress_Singleton implements SecuPress_Scan_Interface { /** Constants. ============================================================================== */ /** * Class version. * * @var (string) */ const VERSION = '1.0.2'; /** Properties. ============================================================================= */ /** * The part of the class that extends this one, like SecuPress_Scan_{$class_name_part}. * * @var (string) */ protected $class_name_part; /** * Contains scan results. * * @var (array) */ protected $result = array(); /** * Contains fix results. * * @var (array) */ protected $result_fix = array(); /** * On multisite, some fixes can't be performed from the network admin. * This array will contain a list of site IDs with scan messages. * * @var (array) */ protected $fix_sites; /** * On multisite, if `$for_current_site` is true, then the scan/fix/etc are performed for the current site, not wetwork-widely. * If needed, should be set right after instanciation. * Does nothing on non-multisite installations. * * @var (bool) */ protected $for_current_site = false; /** * Tells if a scanner is fixable by SecuPress. The value "pro" means it's fixable only with the version PRO. * * @var (bool|string) */ protected $fixable = true; /** * Tells if the fix must occur after all other scans and fixes, while no other scan/fix is running. * * @var (bool) */ protected $delayed_fix = false; /** * Scanner title. * * @var (string) */ public $title = ''; /** * Scan description. * * @var (string) */ public $more = ''; /** * Fix description. * * @var (string) */ public $more_fix = ''; /** Init. =================================================================================== */ /** * Init: this method is required by the class `SecuPress_Singleton`. * * @since 1.0 */ protected function _init() { $this->class_name_part = substr( get_called_class(), 15 ); // 15 is 'SecuPress_Scan_' length. $this->init(); } /** * Sub-classes init. * * @since 1.0 */ protected function init() { die( 'Method SecuPress_Scan->init() must be over-ridden in a sub-class.' ); } /** Multisite specifics. ==================================================================== */ /** * Get `$for_current_site`. * * @since 1.0 * * @return (bool) */ final public function is_for_current_site() { return $this->for_current_site; } /** * `is_network_admin()` does not work in an ajax callback. * * @since 1.0 * * @return (bool) */ final public function is_network_admin() { return is_multisite() && ! $this->is_for_current_site(); } /** * Set `$for_current_site`. * * @since 1.0 * * @param (bool) $for_current_site The new value for `$for_current_site`. * * @return $this */ final public function for_current_site( $for_current_site = null ) { $this->for_current_site = (bool) $for_current_site; return $this; } /** Getters. ================================================================================ */ /** * Tells if a scanner is fixable by SecuPress. The value "pro" means it's fixable only with the version PRO. * * @since 1.0 * * @return (bool|string) */ public function is_fixable() { return $this->fixable; } /** * Tells if the fix must occur after all other scans and fixes, while no other scan/fix is running. * * @since 1.0 * * @return (bool) */ public function is_delayed_fix() { return $this->delayed_fix; } /** * Get the documentation URL. * * @since 1.0 * * @return (string) */ public static function get_docs_url() { return __( 'https://docs.secupress.me/', 'secupress' ); } /** * Get the timeout in seconds, filterable * * @since 1.0.4 * * @return (int) Timeout in seconds */ public static function get_timeout() { /** * Some scan are doing a request on the homepage or some internal files, we need sometimes to raise the timeout * * @since 1.0.4 * * @param (int) Timeout in seconds */ return apply_filters( 'secupress.remote_timeout', 30 ); } /** Messages for scans and fixes. =========================================================== */ /** * Get messages. * * @since 1.0 * * @param (int) $message_id A message ID. */ public static function get_messages( $message_id = null ) { die( 'Method SecuPress_Scan::get_messages() must be over-ridden in a sub-class.' ); } /** Status and messages for scans. ========================================================== */ /** * Maybe set current scan status, only if it isn't set yet. * If the status was already set, only allow to "upgrade" to a superior status. * * @since 1.0 * * @param (string) $status The status code. * @param (bool) $force Set the status, even if it was already set. * * @return (string|bool) The current status. False on failure. */ final protected function set_status( $status, $force = false ) { if ( $this->is_for_current_site() ) { return $this->set_subsite_status( $status, 'scan', 0, $force ); } $statuses = array( 'cantfix' => 0, 'good' => 1, 'warning' => 2, 'bad' => 3, ); // Unkown status. if ( ! isset( $statuses[ $status ] ) ) { return false; } // No previous status. if ( empty( $this->result['status'] ) || $force ) { $this->result['status'] = $status; return $status; } // Status already set: only allow to "upgrade" to a superior status. if ( $statuses[ $status ] > $statuses[ $this->result['status'] ] ) { $this->result['status'] = $status; } return $this->result['status']; } /** * Add a scan message and automatically set the scan status. * * "good": the scan performed correctly and returned a good result. * "warning": the scan could not perform correctly. * "bad": the scan performed correctly but returned a bad result. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. */ final protected function add_message( $message_id, $params = array() ) { if ( $this->is_for_current_site() ) { return $this->add_subsite_message( $message_id, $params ); } $this->result['msgs'] = isset( $this->result['msgs'] ) ? $this->result['msgs'] : array(); $this->result['msgs'][ $message_id ] = $params; $this->set_status( static::get_status_from_message_id( $message_id ) ); } /** * Are scan status and message(s) set? * * @since 1.0 * * @return (bool) */ final protected function has_status() { if ( $this->is_for_current_site() ) { return $this->has_subsite_status(); } return ! empty( $this->result ); } /** * Set a scan status + message only if no status is set yet. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. */ final protected function maybe_set_status( $message_id, $params = array() ) { if ( $this->is_for_current_site() ) { return $this->maybe_set_subsite_status( $message_id, $params ); } if ( ! $this->has_status() ) { $this->add_message( $message_id, $params ); } } /** Status and messages for fixes. ========================================================== */ /** * Maybe set current fix status, only if it isn't set yet. * If the status was already set, only allow to "upgrade" to a superior status. * * @since 1.0 * * @param (string) $status The status code. * @param (bool) $force Set the status, even if it was already set. * * @return (string|bool) The current status. False on failure. */ final protected function set_fix_status( $status, $force = false ) { if ( $this->is_for_current_site() ) { return $this->set_subsite_status( $status, 'fix', 0, $force ); } $statuses = array( 'cantfix' => 0, 'good' => 1, 'warning' => 2, 'bad' => 3, ); // Unkown status. if ( ! isset( $statuses[ $status ] ) ) { return false; } // No previous status. if ( empty( $this->result_fix['status'] ) || $force ) { $this->result_fix['status'] = $status; return $status; } // Status already set: only allow to "upgrade" to a superior status. if ( $statuses[ $status ] > $statuses[ $this->result_fix['status'] ] ) { $this->result_fix['status'] = $status; } return $this->result_fix['status']; } /** * Add a fix message and automatically set the fix status. * * "good": the fix performed correctly. * "warning": partial fix. The fix could not perform entirely: some fix(es) worked and some not. * "bad": error. The fix could not perform correctly. * "cantfix": neutral. The flaw cannot be fixed by this plugin. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. */ public function add_fix_message( $message_id, $params = array() ) { if ( $this->is_for_current_site() ) { return $this->add_subsite_message( $message_id, $params, 'fix' ); } $this->result_fix['msgs'] = isset( $this->result_fix['msgs'] ) ? $this->result_fix['msgs'] : array(); $this->result_fix['msgs'][ $message_id ] = $params; $this->set_fix_status( static::get_status_from_message_id( $message_id ) ); } /** * Are fix status and message(s) set? * * @since 1.0 * * @return (bool) */ final protected function has_fix_status() { if ( $this->is_for_current_site() ) { return $this->has_subsite_status( 'fix' ); } return ! empty( $this->result_fix ); } /** * Set a fix status + message only if no status is set yet. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. */ final protected function maybe_set_fix_status( $message_id, $params = array() ) { if ( $this->is_for_current_site() ) { return $this->maybe_set_subsite_status( $message_id, $params, 'fix' ); } if ( ! $this->has_fix_status() ) { $this->add_fix_message( $message_id, $params ); } } /** * Add a pre fix message to inform the user before a fix (usually because the fix cannot be done). * * "good": the fix performed correctly. * "warning": partial fix. The fix could not perform entirely: some fix(es) worked and some not. * "bad": error. The fix could not perform correctly. * "cantfix": neutral. The flaw cannot be fixed by this plugin. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. */ public function add_pre_fix_message( $message_id, $params = array() ) { if ( $this->is_for_current_site() ) { return $this->add_subsite_message( $message_id, $params ); } $this->result['fix_msg'] = isset( $this->result['fix_msg'] ) ? $this->result['fix_msg'] : array(); $this->result['fix_msg'][ $message_id ] = $params; $this->result_fix['msgs'] = isset( $this->result['fix_msg'] ) ? $this->result['fix_msg'] : array(); $this->result_fix['msgs'][ $message_id ] = $params; $this->update_fix(); } /** Messages for subsites. ================================================================== */ /** * On multisite, some fixes can't be performed from the network admin. * Here we store a list of site IDs with some data: * array( * blog_id => array( * 'scan' => array( * 'status' => 'bad|warning|good', * 'msgs' => array( * message_id => array( data_1 ), * message_id => array( data_1, data_2 ), * ), * ), * 'fix' => array( * 'status' => 'bad|warning|good|cantfix', * 'msgs' => array( * message_id => array( data_1 ), * message_id => array( data_1, data_2 ), * ), * ), * ), * ) */ /** * Maybe set current fix status. * * @since 1.0 * * @param (string) $status The status code. * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. * @param (bool) $force Set the status, even if it was already set. * * @return (string|bool) The current status. False on failure. */ final protected function set_subsite_status( $status, $scan_or_fix = 'scan', $site_id = 0, $force = false ) { $statuses = array( 'cantfix' => 0, 'good' => 1, 'warning' => 2, 'bad' => 3, ); // Unkown status. if ( ! isset( $statuses[ $status ] ) ) { return false; } $scan_or_fix = 'fix' === $scan_or_fix ? 'fix' : 'scan'; $site_id = $site_id ? $site_id : get_current_blog_id(); $this->set_subsite_defaults( $scan_or_fix, $site_id ); // No previous status. if ( empty( $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] ) || $force ) { $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] = $status; } // Status already set: only allow to "upgrade" to a superior status. elseif ( $statuses[ $status ] > $statuses[ $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] ] ) { $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] = $status; } return $this->fix_sites[ $site_id ][ $scan_or_fix ]['status']; } /** * Add a message and automatically set the fix status. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. */ final protected function add_subsite_message( $message_id, $params = array(), $scan_or_fix = 'scan', $site_id = 0 ) { $scan_or_fix = 'fix' === $scan_or_fix ? 'fix' : 'scan'; $site_id = $site_id ? $site_id : get_current_blog_id(); $this->set_subsite_status( static::get_status_from_message_id( $message_id ), $scan_or_fix, $site_id ); $this->fix_sites[ $site_id ][ $scan_or_fix ]['msgs'][ $message_id ] = $params; } /** * Are status and message(s) set? * * @since 1.0 * * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. * * @return (bool) */ final protected function has_subsite_status( $scan_or_fix = 'scan', $site_id = 0 ) { $scan_or_fix = 'fix' === $scan_or_fix ? 'fix' : 'scan'; $site_id = $site_id ? $site_id : get_current_blog_id(); return ! empty( $this->fix_sites[ $site_id ][ $scan_or_fix ] ); } /** * Set a status + message only if no status is set yet. * * @since 1.0 * * @param (int) $message_id The message ID. * @param (array) $params The arguments to use with `vsprintf()`. * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. */ final protected function maybe_set_subsite_status( $message_id, $params = array(), $scan_or_fix = 'scan', $site_id = 0 ) { if ( ! $this->has_subsite_status( $scan_or_fix, $site_id ) ) { $this->add_subsite_message( $message_id, $params, $scan_or_fix, $site_id ); } } /** * Set defaults. * * @since 1.0 * * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. */ final protected function set_subsite_defaults( $scan_or_fix = 'scan', $site_id = 0 ) { $scan_or_fix = 'fix' === $scan_or_fix ? 'fix' : 'scan'; $site_id = $site_id ? $site_id : get_current_blog_id(); $this->set_empty_data_for_subsite( $site_id ); $this->fix_sites[ $site_id ][ $scan_or_fix ] = isset( $this->fix_sites[ $site_id ][ $scan_or_fix ] ) ? $this->fix_sites[ $site_id ][ $scan_or_fix ] : array(); $this->fix_sites[ $site_id ][ $scan_or_fix ]['msgs'] = isset( $this->fix_sites[ $site_id ][ $scan_or_fix ]['msgs'] ) ? $this->fix_sites[ $site_id ][ $scan_or_fix ]['msgs'] : array(); $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] = isset( $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] ) ? $this->fix_sites[ $site_id ][ $scan_or_fix ]['status'] : ''; } /** * Remove all previously stored messages for sub-sites. * This will allow to set an empty transient, and then empty the option later. * * @since 1.0 */ final protected function set_empty_data_for_subsites() { $this->fix_sites = array_fill_keys( static::get_blog_ids(), array() ); } /** * Remove all previously stored messages for a particular sub-site. * This will allow to set an empty transient, and then empty the option later for this sub-site. * * @since 1.0 * * @param (int) $site_id The site ID. */ final protected function set_empty_data_for_subsite( $site_id = 0 ) { $site_id = $site_id ? $site_id : get_current_blog_id(); $this->fix_sites = is_array( $this->fix_sites ) ? $this->fix_sites : array(); $this->fix_sites[ $site_id ] = isset( $this->fix_sites[ $site_id ] ) ? $this->fix_sites[ $site_id ] : array(); } /** * Get a sub-site scan/fix results. * * @since 1.0 * * @param (string) $scan_or_fix For a scan or a fix. * @param (int) $site_id The site ID. * * @return (array) */ final protected function get_subsite_result( $scan_or_fix = 'scan', $site_id = 0 ) { $scan_or_fix = 'fix' === $scan_or_fix ? 'fix' : 'scan'; $scan_or_fix_invert = 'fix' === $scan_or_fix ? 'scan' : 'fix'; $site_id = $site_id ? $site_id : get_current_blog_id(); $result = $this->fix_sites[ $site_id ][ $scan_or_fix ]; unset( $this->fix_sites[ $site_id ][ $scan_or_fix ] ); if ( ! empty( $this->fix_sites[ $site_id ][ $scan_or_fix_invert ] ) ) { return $result; } unset( $this->fix_sites[ $site_id ] ); if ( ! empty( $this->fix_sites ) ) { return $result; } $this->fix_sites = null; return $result; } /** Scan and fix. =========================================================================== */ /** * Scan for flaw(s). * * @since 1.0 * * @return (array) The scan results. */ public function scan() { $this->update(); if ( $this->is_for_current_site() ) { $result = $this->get_subsite_result(); } else { $result = $this->result; $this->result = array(); } return $result; } /** * Try to fix the flaw(s). * * @since 1.0 * * @return (array) The fix results. */ public function fix() { $this->update_fix(); if ( $this->is_for_current_site() ) { $result = $this->get_subsite_result( 'fix' ); } else { $result = $this->result_fix; $this->result_fix = array(); } return $result; } /** * Return an array of actions if a manual fix is needed here. False otherwise. * In case a scanner with a manual fix doesn't need to be fixed, return an empty array instead of false: this way, this scanner will never be listed in the automatic fixes (if the scan is not up to date for example). * * @since 1.0 * * @return (bool|array) */ public function need_manual_fix() { return false; } /** * Try to fix the flaw(s) after requiring user action. * * @since 1.0 * * @return (array) The fix results. */ public function manual_fix() { $this->update_fix(); if ( defined( 'DOING_AJAX' ) ) { $fix_actions = $this->need_manual_fix(); if ( $fix_actions ) { // Add the fixes that require user action in the returned data. $form = $this->get_required_fix_action_template_parts( $fix_actions, false ); if ( $this->is_for_current_site() ) { $site_id = get_current_blog_id(); $this->set_subsite_defaults( 'fix', $site_id ); $this->fix_sites[ $site_id ]['fix']['form_contents'] = $form; } else { $this->result_fix['form_contents'] = $form; } } } if ( $this->is_for_current_site() ) { $result = $this->get_subsite_result( 'fix' ); } else { $result = $this->result_fix; $this->result_fix = array(); } return $result; } /** * Get ONLY THE REQUIRED forms that would fix the scan if it requires user action. * * @since 1.0 * * @param (array) $fix_actions An array of fix actions. * @param (bool) $echo True to print the outpout, False to return it. * * @return (string) A string of HTML templates (form contents most of the time). */ final public function get_required_fix_action_template_parts( $fix_actions, $echo = true ) { $templates = array_intersect_key( $this->get_fix_action_template_parts(), $fix_actions ); $templates = implode( '', $templates ); if ( $templates ) { $templates .= $this->get_fix_action_fields( $fix_actions ); } if ( ! $echo ) { return $templates; } echo $templates; } /** * Get an array containing ALL the forms that would fix the scan if it requires user action. * * @since 1.0 * * @return (array) An array of HTML templates (form contents most of the time). */ protected function get_fix_action_template_parts() { return array(); } /** * Tell if a fix action part is needed. * * @since 1.0 * * @param (string) $fix_id A fix action identifier. * * @return (bool) */ final protected function has_fix_action_part( $fix_id ) { $fix_ids = ! empty( $_POST['test-parts'] ) ? ',' . $_POST['test-parts'] . ',' : ''; // WPCS: CSRF ok. return false !== strpos( $fix_ids, ',' . $fix_id . ',' ); } /** * Get the required fields for the user fix form (nonce, referrer, action, etc). * * @since 1.0 * * @param (array) $fix_actions An array of fix actions. * * @return (string) */ final public function get_fix_action_fields( $fix_actions ) { $nonce = 'secupress_manual_fixit_' . $this->class_name_part; $output = "\n"; if ( $this->is_for_current_site() ) { $nonce .= '-' . get_current_blog_id(); $output .= '<input type="hidden" name="for-current-site" value="1" />' . "\n"; $output .= '<input type="hidden" name="site" value="' . get_current_blog_id() . '" />' . "\n"; } $output .= '<input type="hidden" name="action" value="secupress_manual_fixit" />' . "\n"; $output .= '<input type="hidden" name="test" value="' . $this->class_name_part . '" />' . "\n"; $output .= '<input type="hidden" name="test-parts" value="' . implode( ',', $fix_actions ) . '" />' . "\n"; $output .= '<input type="hidden" name="_wpnonce" value="' . wp_create_nonce( $nonce ) . '" />' . "\n"; $output .= '<input type="hidden" name="_wp_http_referer" value="' . esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) ) . '" />' . "\n"; return $output; } /** Options. ================================================================================ */ /** * Set options: scan results. * * @since 1.0 * * @return (array) Scan results. */ final public function update() { $name = strtolower( $this->class_name_part ); if ( $this->is_for_current_site() ) { if ( ! isset( $this->fix_sites ) ) { return array(); } $site_id = get_current_blog_id(); $sub_results = SecuPress_Scanner_Results::get_sub_sites_results( $name ); $sub_results = is_array( $sub_results ) ? $sub_results : array(); $sub_results[ $site_id ] = ! empty( $sub_results[ $site_id ] ) ? $sub_results[ $site_id ] : array(); $sub_results[ $site_id ]['scan'] = ! empty( $this->fix_sites[ $site_id ]['scan'] ) ? $this->fix_sites[ $site_id ]['scan'] : array(); SecuPress_Scanner_Results::update_sub_sites_result( $name, $sub_results ); return isset( $this->fix_sites[ $site_id ]['scan'] ) && is_array( $this->fix_sites[ $site_id ]['scan'] ) ? $this->fix_sites[ $site_id ]['scan'] : array(); } SecuPress_Scanner_Results::update_scan_result( $name, $this->result ); return $this->result; } /** * Set options: fix results. * * @since 1.0 * * @return (array) Fix results. */ final public function update_fix() { $name = strtolower( $this->class_name_part ); if ( $this->is_for_current_site() ) { if ( ! isset( $this->fix_sites ) ) { return array(); } $site_id = get_current_blog_id(); $sub_results = SecuPress_Scanner_Results::get_sub_sites_results( $name ); $sub_results = is_array( $sub_results ) ? $sub_results : array(); $sub_results[ $site_id ] = ! empty( $sub_results[ $site_id ] ) ? $sub_results[ $site_id ] : array(); $sub_results[ $site_id ]['fix'] = ! empty( $this->fix_sites[ $site_id ]['fix'] ) ? $this->fix_sites[ $site_id ]['fix'] : array(); SecuPress_Scanner_Results::update_sub_sites_result( $name, $sub_results ); return isset( $this->fix_sites[ $site_id ]['fix'] ) && is_array( $this->fix_sites[ $site_id ]['fix'] ) ? $this->fix_sites[ $site_id ]['fix'] : array(); } if ( isset( $this->fix_sites ) ) { SecuPress_Scanner_Results::update_sub_sites_result( $name, $this->fix_sites ); } SecuPress_Scanner_Results::update_fix_result( $name, $this->result_fix ); return $this->result_fix; } /** Other transients. ======================================================================= */ /** * Auto-scan: schedule an auto-scan that will be executed on page load. * * @since 1.0 */ final public function schedule_autoscan() { if ( $this->is_for_current_site() ) { $transient = secupress_get_transient( 'secupress_autoscans' ); $transient = is_array( $transient ) ? $transient : array(); $transient[ $this->class_name_part ] = $this->class_name_part; secupress_set_transient( 'secupress_autoscans', $transient ); } else { $transient = secupress_get_site_transient( 'secupress_autoscans' ); $transient = is_array( $transient ) ? $transient : array(); $transient[ $this->class_name_part ] = $this->class_name_part; secupress_set_site_transient( 'secupress_autoscans', $transient ); } } /** * Auto-scan: get auto-scans and delete the transient used to store them. * * @since 1.0 * * @return (array) Scan results. */ final public static function get_and_delete_autoscans() { if ( is_multisite() && ! is_network_admin() ) { $transient = secupress_get_transient( 'secupress_autoscans' ); if ( false !== $transient ) { secupress_delete_transient( 'secupress_autoscans' ); } } else { $transient = secupress_get_site_transient( 'secupress_autoscans' ); if ( false !== $transient ) { secupress_delete_site_transient( 'secupress_autoscans' ); } } return is_array( $transient ) ? $transient : array(); } /** Tools. ================================================================================== */ /** * Given an array of items, wrap them in a HTML tag. * * @since 1.0 * * @param (array) $items An array of items. * @param (string) $tag The tag. * * @return (array). */ final public static function wrap_in_tag( $items, $tag = 'code' ) { if ( $items ) { $items = (array) $items; foreach ( $items as $k => $item ) { $items[ $k ] = sprintf( '<%2$s>%1$s</%2$s>', $item, $tag ); } } return $items ? $items : array(); } /** * Slice the items if there are too many. Will add a "XX others" item. * * @since 1.0 * * @param (array) $items An array of items. * @param (int) $max_count The maximum length of the array. */ final public static function slice_and_dice( &$items, $max_count ) { $count = count( $items ) - $max_count; if ( $count > 0 ) { $items = array_slice( $items, 0, $max_count ); array_push( $items, sprintf( _n( '%s other', '%s others', $count ), number_format_i18n( $count ) ) ); } } /** * Get the status code from a message identifier. * * @since 1.0 * * @param (int) $message_id The message ID. */ final protected static function get_status_from_message_id( $message_id ) { if ( $message_id < 100 ) { return 'good'; } if ( $message_id < 200 ) { return 'warning'; } if ( $message_id < 300 ) { return 'bad'; } if ( $message_id < 400 ) { return 'cantfix'; } } /** * Get the list of the blog IDs. * * @since 1.0 * * @return (array) */ final protected static function get_blog_ids() { global $wpdb; static $blogs; if ( isset( $blogs ) ) { return $blogs; } $blogs = $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ) ); $blogs = array_map( 'absint', $blogs ); return $blogs; } /** * A shorthand to get the default arguments to use in `wp_remote_get()` and friends. * * @since 1.1.3 * @author Grégory Viguier * * @return (array) An array of default arguments. */ protected function get_default_request_args() { $class_name = get_class( $this ); $request_args = array( 'redirection' => 0, 'timeout' => static::get_timeout(), 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), 'user-agent' => SECUPRESS_PLUGIN_NAME . '/' . SECUPRESS_VERSION, 'cookies' => $_COOKIE, 'headers' => array( 'X-SecuPress-Origin' => $class_name, ), ); /** * Filter the default arguments used in the scanners' internal requests. * * @since 1.1.3 * * @param (array) $request_args The request arguments. * @param (string) $class_name The scan class name. */ return apply_filters( 'secupress.scan.default_request_args', $request_args, $class_name ); } /** * A sandbox for doing crazy things with `.htaccess`. * Create a folder containing a `.htaccess` file with the provided content and a `secupress.html` file. * Then, make a request to the `secupress.html` file to test if a server error is triggered. * * @since 1.0 * * @param (string) $content The content to put in the `.htaccess` file. * * @return (object|bool) Return true if the server does not trigger an error 500, false otherwise. * Return a WP_Error object if the sandbox creation fails or if the HTTP request fails. */ final protected function htaccess_success_in_sandbox( $content ) { /** * Allows to bypass the sandbox * @param (bool) true by default, false to use it. * @param (string) A context. */ if ( false === apply_filters( 'secupress.use_sandbox', true, 'htaccess' ) ) { return true; } $wp_filesystem = secupress_get_filesystem(); $folder_name = 'secupress-sandbox-' . uniqid(); $folder_path = ABSPATH . '/' . $folder_name; // Create folder. if ( ! $wp_filesystem->mkdir( $folder_path ) ) { return new WP_Error( 'dir_creation_failed', __( 'The temporary directory could not be created.', 'secupress' ) ); } // Create `secupress.html` file. if ( ! $wp_filesystem->put_contents( $folder_path . '/secupress.html', 'You are here.', FS_CHMOD_FILE ) ) { $wp_filesystem->delete( $folder_path, true ); return new WP_Error( 'file_creation_failed', __( 'The temporary directory could not be created.', 'secupress' ) ); } // Create `.htaccess` file with our content. if ( ! $wp_filesystem->put_contents( $folder_path . '/.htaccess', $content, FS_CHMOD_FILE ) ) { $wp_filesystem->delete( $folder_path, true ); return new WP_Error( 'htaccess_creation_failed', __( 'The temporary directory could not be created.', 'secupress' ) ); } // Try to reach `secupress.html`. $response = wp_remote_get( site_url( $folder_name . '/secupress.html' ), $this->get_default_request_args() ); // Now we can get rid of the files. $wp_filesystem->delete( $folder_path, true ); // HTTP requests are probably blocked. if ( is_wp_error( $response ) ) { return $response; } // Finally, the answer we were looking for. return 500 !== wp_remote_retrieve_response_code( $response ); } /** * Extract a `<pre/>` content from a message. * * @since 1.0 * * @param (array) $error An array where `message` is the key of our message. * * @return (string) The `<pre/>` tag and its content, or `<code>Error</code>` if no `<pre/>` tag is found. */ final protected static function get_rules_from_error( $error ) { $rules = '<code>Error</code>'; if ( preg_match( '@<pre>.+</pre>@ms', $error['message'], $matches ) ) { $rules = $matches[0]; } return $rules; } /** * Extract a `<code/>` content from a message. * * @since 1.0 * @author Grégory Viguier * * @param (array) $error An array where `message` is the key of our message. * @param (string) $class If not empty, the `<code>` tag with this specific html class will be searched. * * @return (string) The `<code/>` tag and its content, or an empty string if no `<code/>` tag is found. */ final protected static function get_code_tag_from_error( $error, $class = '' ) { $class = $class ? ' class="' . $class . '"' : ''; $tag = ''; if ( preg_match( '@<code' . $class . '>.+</code>@ms', $error['message'], $matches ) ) { $tag = $matches[0]; } return $tag; } /** * Multisite: tell if the "centralized blog options" are fully filled. * * @since 1.0 * @author Grégory Viguier * * @return (bool) True if our network options contain all blog options, or if it's not a multisite. False otherwise. */ final protected static function are_centralized_blog_options_filled() { if ( ! is_multisite() ) { return true; } $plugins = get_site_option( 'secupress_active_plugins' ); return is_array( $plugins ) && empty( $plugins['offset'] ); } /** * Filter every scan to bypass the scan and return "true" * * @param (string) $class The SecuPress class to be filtered. * * @return (bool) "false" by default (not modified), should be "true" to be used * @author Julio Potier **/ final protected function filter_scanner( $class ) { return ! is_null( apply_filters( 'secupress.pre_scan.' . $class, null ) ); } }
Edit
Rename
Chmod
Delete
FILE
FOLDER
Name
Size
Permission
Action
class-secupress-scan-admin-user.php
11163 bytes
0644
class-secupress-scan-auto-update.php
8307 bytes
0644
class-secupress-scan-bad-config-files.php
6820 bytes
0644
class-secupress-scan-bad-file-extensions.php
10841 bytes
0644
class-secupress-scan-bad-old-files.php
5834 bytes
0644
class-secupress-scan-bad-old-plugins.php
28686 bytes
0644
class-secupress-scan-bad-request-methods.php
5590 bytes
0644
class-secupress-scan-bad-url-access.php
9461 bytes
0644
class-secupress-scan-bad-user-agent.php
5424 bytes
0644
class-secupress-scan-bad-usernames.php
5640 bytes
0644
class-secupress-scan-bad-vuln-plugins.php
8579 bytes
0644
class-secupress-scan-chmods.php
10942 bytes
0644
class-secupress-scan-core-update.php
6257 bytes
0644
class-secupress-scan-db-prefix.php
9916 bytes
0644
class-secupress-scan-directory-listing.php
9202 bytes
0644
class-secupress-scan-discloses.php
18508 bytes
0644
class-secupress-scan-easy-login.php
5278 bytes
0644
class-secupress-scan-https.php
8735 bytes
0644
class-secupress-scan-inactive-plugins-themes.php
21581 bytes
0644
class-secupress-scan-login-errors-disclose.php
4833 bytes
0644
class-secupress-scan-passwords-strength.php
6194 bytes
0644
class-secupress-scan-php-404.php
4768 bytes
0644
class-secupress-scan-php-disclosure.php
9027 bytes
0644
class-secupress-scan-phpversion.php
4316 bytes
0644
class-secupress-scan-plugins-update.php
7140 bytes
0644
class-secupress-scan-readme-discloses.php
10676 bytes
0644
class-secupress-scan-salt-keys.php
7316 bytes
0644
class-secupress-scan-shellshock.php
7317 bytes
0644
class-secupress-scan-sqli.php
5034 bytes
0644
class-secupress-scan-subscription.php
10080 bytes
0644
class-secupress-scan-themes-update.php
7065 bytes
0644
class-secupress-scan-woocommerce-discloses.php
7260 bytes
0644
class-secupress-scan-wp-config.php
12860 bytes
0644
class-secupress-scan-wpml-discloses.php
7190 bytes
0644
class-secupress-scan-wporg.php
3714 bytes
0644
class-secupress-scan.php
34265 bytes
0644
N4ST4R_ID | Naxtarrr