Server IP : 104.21.14.48 / Your IP : 18.223.107.85 [ Web Server : Apache System : Linux b70eb322-3aee-0c53-7c82-0db91281f2c6.secureserver.net 6.1.90-1.el9.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 2 12:09:22 EDT 2024 x86_64 User : root ( 0) PHP Version : 8.0.30.2 Disable Function : NONE Domains : 0 Domains MySQL : ON | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/wp-content/plugins/defender-security/src/controller/ |
Upload File : |
<?php /** * Handles security tweaks operations. * * @package WP_Defender\Controller */ namespace WP_Defender\Controller; use Countable; use WP_Defender\Event; use WP_Defender\Admin; use Calotes\Component\Request; use Calotes\Component\Response; use Calotes\Helper\Array_Cache; use WP_Defender\Component\Rate; use WP_Defender\Model\Notification\Tweak_Reminder; use WP_Defender\Component\Config\Config_Hub_Helper; use WP_Defender\Component\Security_Tweaks\Hide_Error; use WP_Defender\Component\Security_Tweaks\WP_Version; use WP_Defender\Component\Security_Tweaks\PHP_Version; use WP_Defender\Component\Security_Tweaks\Prevent_PHP; use WP_Defender\Component\Security_Tweaks\Change_Admin; use WP_Defender\Component\Security_Tweaks\Security_Key; use WP_Defender\Component\Security_Tweaks\Login_Duration; use WP_Defender\Component\Security_Tweaks\Servers\Server; use WP_Defender\Component\Security_Tweaks\Disable_XML_RPC; use WP_Defender\Component\Security_Tweaks\Disable_Trackback; use WP_Defender\Component\Security_Tweaks\Prevent_Enum_Users; use WP_Defender\Component\Security_Tweaks\Disable_File_Editor; use WP_Defender\Component\Security_Tweaks\Protect_Information; use WP_Defender\Model\Setting\Security_Tweaks as Model_Security_Tweaks; /** * Contains methods for processing, reverting, ignoring security recommendations, and updating security reminders. */ class Security_Tweaks extends Event { /** * The slug identifier for this controller. * * @var string */ public $slug = 'wdf-hardener'; /** * The model for handling the data. * * @var Model_Security_Tweaks */ protected $model; /** * The Scan component, responsible for scanning the site. * * @var \WP_Defender\Component\Scan */ public $scan; /** * Components instance array. * * @var array */ private $component_instances; /** * Instance of Security_Key. * * @var Security_Key */ private $security_key; /** * Instance of Prevent_Enum_Users. * * @var Prevent_Enum_Users */ private $prevent_enum_users; public const STATUS_ISSUES = 'issues', STATUS_RESOLVE = 'fixed', STATUS_IGNORE = 'ignore', STATUS_RESTORE = 'restore'; /** * Initializes the model and service, registers routes, and sets up scheduled events if the model is active. */ public function __construct() { $this->register_page( esc_html__( 'Recommendations', 'defender-security' ), $this->slug, array( & $this, 'main_view', ), $this->parent_slug ); $this->model = wd_di()->get( Model_Security_Tweaks::class ); $this->register_routes(); // Init all the tweaks, should happen one time. $this->component_instances = $this->init_tweaks(); $this->scan = wd_di()->get( \WP_Defender\Component\Scan::class ); $this->security_key = $this->component_instances['security-key']; $this->prevent_enum_users = $this->component_instances['prevent-enum-users']; // Now shield up. $this->boot(); // Add addition hooks. add_action( 'defender_enqueue_assets', array( &$this, 'enqueue_assets' ) ); add_action( 'wp_loaded', array( &$this, 'should_output_error' ) ); } /** * Dummy function for testing a check. */ public function should_output_error() { if ( empty( defender_get_data_from_request( 'defender_test_error_reporting', 'g' ) ) ) { return; } // It should be only trigger by admin. if ( ! $this->check_permission() ) { return; } $var = '$' . uniqid( '', true ); // This should output a warning. Ignored phpcs as it's a test. echo ${$var}; // phpcs:ignore exit(); } /** * Process. * * @param Request $request Request object. * * @return Response * @defender_route */ public function process( Request $request ) { $data = $request->get_data( array( 'slug' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), 'current_server' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slug = $data['slug'] ?? false; $tweak = $this->get_tweak( $slug ); if ( ! is_object( $tweak ) ) { return new Response( false, array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) ); } if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) { $current_server = $data['current_server'] ?? false; if ( ! $current_server ) { return new Response( false, array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) ); } $ret = $tweak->process( $current_server ); } else { $ret = $tweak->process(); } if ( true === $ret ) { Config_Hub_Helper::set_clear_active_flag(); $this->model->mark( self::STATUS_RESOLVE, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Actioned' ); // Response. $this->ajax_response( esc_html__( 'Security recommendation successfully resolved.', 'defender-security' ) ); } if ( is_wp_error( $ret ) ) { $this->ajax_response( $ret->get_error_message(), false ); } return new Response( false, array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) ); } /** * Revert. * * @param Request $request Request object. * * @return Response * @defender_route */ public function revert( Request $request ) { $data = $request->get_data( array( 'slug' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), 'current_server' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slug = $data['slug'] ?? false; $tweak = $this->get_tweak( $slug ); $invalid = array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ); if ( ! is_object( $tweak ) ) { return new Response( false, $invalid ); } if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) { $current_server = $data['current_server'] ?? false; if ( ! $current_server ) { return new Response( false, $invalid ); } $ret = $tweak->revert( $current_server ); } else { $ret = $tweak->revert(); } if ( is_wp_error( $ret ) ) { $this->ajax_response( $ret->get_error_message(), false ); } if ( true === $ret ) { Config_Hub_Helper::set_clear_active_flag(); $this->model->mark( self::STATUS_ISSUES, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Reverted' ); // Response. $this->ajax_response( esc_html__( 'Security recommendation successfully reverted.', 'defender-security' ) ); } return new Response( false, $invalid ); } /** * Ignore. * * @param Request $request Request object. * * @return Response|void * @defender_route */ public function ignore( Request $request ) { $data = $request->get_data( array( 'slug' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slug = $data['slug'] ?? false; $tweak = $this->get_tweak( $slug ); if ( ! is_object( $tweak ) ) { return new Response( false, array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) ); } $this->model->mark( self::STATUS_IGNORE, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Ignored' ); $this->security_key->cron_unschedule(); $this->ajax_response( esc_html__( 'Security recommendation successfully ignored.', 'defender-security' ) ); } /** * Restore. * * @param Request $request Request object. * * @return Response|void * @defender_route */ public function restore( Request $request ) { $data = $request->get_data( array( 'slug' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slug = $data['slug'] ?? false; $tweak = $this->get_tweak( $slug ); if ( ! is_object( $tweak ) ) { return new Response( false, array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) ); } $this->model->mark( self::STATUS_RESTORE, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Restored' ); if ( $this->security_key->get_is_autogenerate_keys() ) { // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job. $this->security_key->cron_unschedule(); $this->security_key->cron_schedule(); } $this->ajax_response( esc_html__( 'Security recommendation successfully restored.', 'defender-security' ) ); } /** * Recheck. * * @param Request $request Request object. * * @return Response * @defender_route */ public function recheck( Request $request ): Response { $data = $request->get_data( array( 'slug' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slug = $data['slug'] ?? false; $tweak = $this->get_tweak( $slug ); if ( ! is_object( $tweak ) ) { return new Response( false, array( 'message' => esc_html__( 'The status cannot be verified as the request contains an invalid slug.', 'defender-security' ), ) ); } $ret = $tweak->check(); if ( true === $ret ) { $this->ajax_response( esc_html__( 'Security recommendation successfully resolved.', 'defender-security' ), true, 1 ); } if ( is_wp_error( $ret ) ) { return new Response( false, array( 'message' => $ret->get_error_message() ) ); } $msg = sprintf( /* translators: %s: Tweak title. */ esc_html__( '%s is not actioned. Please ensure that all the instructions are followed.', 'defender-security' ), $tweak->to_array()['title'] ?? '' ); return new Response( false, array( 'message' => $msg ) ); } /** * Update security reminder. * * @param Request $request Request object. * * @return Response * @defender_route */ public function update_security_reminder( Request $request ): Response { $data = $request->get_data(); $remind_date = $data['remind_date'] ?? false; $is_autogen_flag = isset( $data['is_autogenerate_keys'] ) ? filter_var( $data['is_autogenerate_keys'], FILTER_VALIDATE_BOOLEAN ) : false; if ( ! $remind_date ) { return new Response( false, array( 'message' => esc_html__( 'Invalid Reminder frequency', 'defender-security' ) ) ); } $values = array( 'reminder_duration' => $remind_date, 'reminder_date' => strtotime( '+' . $remind_date, time() ), 'is_autogenerate_keys' => $is_autogen_flag, ); if ( update_site_option( 'defender_security_tweaks_' . $this->security_key->slug, $values ) ) { if ( true === $is_autogen_flag ) { // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job. $this->security_key->cron_unschedule(); $this->security_key->cron_schedule(); } return new Response( true, array( 'message' => esc_html__( 'Security recommendation successfully updated.', 'defender-security' ) ) ); } else { return new Response( false, array( 'message' => esc_html__( 'Error while updating.', 'defender-security' ) ) ); } } /** * AJAX Response handler * * @param string $message The message to be displayed in the response. * @param bool $is_success Whether the response should have a success status. * @param bool|int $interval The cron interval for the security key autogeneration. Default is false. * * @return Response The AJAX response. */ private function ajax_response( $message, $is_success = true, $interval = false ): Response { global $wp_version; $settings = new Model_Security_Tweaks(); $data = array( 'message' => $message, 'summary' => array( 'issues_count' => count( $settings->issues ), 'fixed_count' => count( $settings->fixed ), 'ignore_count' => count( $settings->ignore ), 'php_version' => PHP_VERSION, 'wp_version' => $wp_version, ), 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ), 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ), 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ), 'issues_slugs' => $settings->issues, ); if ( $interval ) { $data['interval'] = $interval; } return new Response( $is_success, $data ); } /** * Enqueues scripts and styles for this page. * Only enqueues assets if the page is active. */ public function enqueue_assets() { if ( ! $this->is_page_active() ) { return; } wp_localize_script( 'def-securitytweaks', 'security_tweaks', $this->data_frontend() ); wp_enqueue_script( 'def-securitytweaks' ); $this->enqueue_main_assets(); } /** * Provides data for the frontend. * * @return array An array of data for the frontend. */ public function data_frontend(): array { $this->refresh_tweaks_status(); global $wp_version; $not_allowed_bulk = array( 'php-version', 'replace-admin-username', ); if ( 'nginx' === Server::get_current_server() ) { $not_allowed_bulk[] = 'protect-information'; $not_allowed_bulk[] = 'prevent-php-executed'; } $tweak_arr = $this->model->get_tweak_types(); $total_tweaks = $tweak_arr['count_fixed'] + $tweak_arr['count_ignored'] + $tweak_arr['count_issues']; // Prepare additional data. if ( wd_di()->get( Admin::class )->is_wp_org_version() ) { $misc = array( 'rating_is_displayed' => ! Rate::was_rate_request() && $tweak_arr['count_fixed'] === $total_tweaks, 'rating_text' => sprintf( /* translators: %d - Total number. */ esc_html__( 'You`ve resolved all %d security recommendations - that`s impressive! We are happy to be a part of helping you secure your site, and we would appreciate it if you dropped us a rating on wp.org to help us spread the word and boost our motivation.', 'defender-security' ), $total_tweaks ), 'rating_type' => 'tweak', ); } else { $misc = array( 'rating_is_displayed' => false, 'rating_text' => '', 'rating_type' => '', ); } $data = array( 'summary' => array( 'fixed_count' => $tweak_arr['count_fixed'], 'ignore_count' => $tweak_arr['count_ignored'], 'issues_count' => $tweak_arr['count_issues'], 'php_version' => PHP_VERSION, 'wp_version' => $wp_version, ), 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ), 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ), 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ), 'not_allowed_bulk' => $not_allowed_bulk, 'indicator_issue_count' => $this->scan->indicator_issue_count(), 'is_autogenerate_keys' => $this->security_key->get_is_autogenerate_keys(), 'reminder_frequencies' => $this->security_key->reminder_frequencies(), 'enabled_user_enums' => $this->prevent_enum_users->get_enabled_user_enums(), 'misc' => $misc, ); return array_merge( $data, $this->dump_routes_and_nonces() ); } /** * Renders the main view for this page. */ public function main_view() { $this->render( 'main' ); } /** * Bulk action for security tweaks. * Handles bulk resolving, ignoring, or reverting security tweaks. * * @param Request $request The request object. * * @return Response * @defender_route */ public function bulk_action( Request $request ) { $data = $request->get_data( array( 'slugs' => array( 'type' => 'array', 'sanitize' => 'sanitize_text_field', ), 'intention' => array( 'type' => 'string', 'sanitize' => 'sanitize_text_field', ), ) ); $slugs = $data['slugs'] ?? array(); $intention = $data['intention'] ?? false; // Get processed and unprocessed tweaks. [ $processed, $unprocessed ] = $this->security_tweaks_auto_action( $slugs, $intention ); $message = sprintf( /* translators: 1: Either ignored or resolved, 2: Count security recommendations */ esc_html__( 'You have bulk %1$s %2$s security recommendations.', 'defender-security' ), 'ignore' === $intention ? 'ignored' : 'resolved', $processed ); if ( isset( $unprocessed ) && $unprocessed > 0 ) { // If we have this case this mean the intention is resolved. $message = sprintf( /* translators: %d: Count security tweaks */ esc_html__( 'You have bulk actioned %d security recommendations. You still have a few unresolved security recommendations, which cannot be bulk actioned automatically, so please address them below.', 'defender-security' ), $processed ); Config_Hub_Helper::set_clear_active_flag(); } return $this->ajax_response( $message ); } /** * Mass processing. * * @param array $slugs Tweak slugs. * @param string $intention Status. * * @return array */ public function security_tweaks_auto_action( $slugs, $intention ): array { $processed = 0; $unprocessed = 0; foreach ( $slugs as $slug ) { $tweak = $this->get_tweak( $slug ); if ( 'ignore' === $intention ) { $this->model->mark( self::STATUS_IGNORE, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Ignored' ); } elseif ( 'resolve' === $intention ) { $wont_do = array( 'replace-admin-username', 'prevent-php-executed', 'wp-version', 'php-version', 'protect-information', ); if ( in_array( $slug, $wont_do, true ) ) { ++$unprocessed; continue; } if ( $tweak->has_method( 'bulk_process' ) ) { $ret = $tweak->bulk_process(); } else { $ret = $tweak->process(); } if ( is_wp_error( $ret ) ) { $data = $tweak->to_array(); $this->ajax_response( sprintf( /* translators: 1: Security tweak title, 2: Error message */ esc_html__( 'There is an error while processing recommendation %1$s, error message: %2$s', 'defender-security' ), $data['title'], $ret->get_error_message() ), false ); } $this->model->mark( self::STATUS_RESOLVE, $slug ); // Track. $this->track_tweak( $tweak->to_array()['title'], 'Actioned' ); } ++$processed; } return array( $processed, $unprocessed ); } /** * Refresh the tweak status and save their state. * * @return void */ public function refresh_tweaks_status() { $tweaks = $this->init_tweaks(); $settings = new Model_Security_Tweaks(); $fixed = array(); $issues = array(); foreach ( $tweaks as $slug => $class ) { if ( $settings->is_tweak_ignore( $slug ) ) { continue; } $is_resolved = $class->check(); if ( $is_resolved ) { $fixed[] = $slug; } else { $issues[] = $slug; } } $settings->fixed = $fixed; $settings->issues = $issues; $settings->save(); } /** * This function for shield every active tweaks up, we will use the cached result. * No check function trigger in this init runtime. */ private function boot() { $tweaks = $this->init_tweaks( self::STATUS_RESOLVE ); foreach ( $tweaks as $tweak ) { $tweak->shield_up(); } } /** * Instance all the tweaks, happen one time in init runtime. * * @param string $type Type of tweaks. * @param string $format Object for internal use, array for frontend use. * * @return array */ public function init_tweaks( $type = '', $format = 'object' ): array { $classes = array( Disable_XML_RPC::class, WP_Version::class, Hide_Error::class, PHP_Version::class, Change_Admin::class, Security_Key::class, Login_Duration::class, Disable_Trackback::class, Prevent_Enum_Users::class, Disable_File_Editor::class, ); if ( ! defender_is_wp_cli() ) { // We don't load this in cli, as clearly no server is running. $classes = array_merge( $classes, array( Protect_Information::class, Prevent_PHP::class, ) ); } $tweaks = Array_Cache::get( 'tweaks', 'tweaks' ); if ( ! is_array( $tweaks ) ) { foreach ( $classes as $class ) { $obj = new $class(); $tweaks[ $obj->slug ] = $obj; } Array_Cache::set( 'tweaks', $tweaks, 'tweaks' ); } $tmp = array(); if ( empty( $type ) ) { $tmp = $tweaks; } else { $settings = new Model_Security_Tweaks(); $compare = $settings->$type; foreach ( $compare as $slug ) { if ( isset( $tweaks[ $slug ] ) ) { $tmp[ $slug ] = $tweaks[ $slug ]; } } } if ( 'array' === $format ) { // We need to parse this as array. foreach ( $tmp as $slug => $obj ) { $arr = $obj->to_array(); $arr['status'] = $type; $tmp[ $slug ] = $arr; } } return $tmp; } /** * Get tweak object from cache by slug. * * @param string $slug Tweak slug. * * @return mixed Tweak object if exist, else null. */ private function get_tweak( $slug ) { $tweaks = Array_Cache::get( 'tweaks', 'tweaks' ); return $tweaks[ $slug ] ?? null; } /** * Converts the current object state to an array. * * @return array The array representation of the object. */ public function to_array(): array { $this->refresh_tweaks_status(); $settings = new Model_Security_Tweaks(); return array( 'rules' => array_slice( $this->init_tweaks( self::STATUS_ISSUES, 'array' ), 0, 5 ), 'count' => array( 'issues' => count( $settings->issues ), 'resolved' => count( $settings->fixed ), 'total' => count( $this->init_tweaks() ), ), ); } /** * Removes settings for all submodules. */ public function remove_settings() { // Revert it first. $tweaks = $this->init_tweaks( self::STATUS_RESOLVE ); // Assign this so internal can use the current server. $_POST['current_server'] = Server::get_current_server(); foreach ( $tweaks as $tweak ) { $tweak->revert(); if ( method_exists( $tweak, 'delete_all_option' ) ) { $tweak->delete_all_option(); } } ( new Model_Security_Tweaks() )->delete(); delete_site_transient( Server::CACHE_CURRENT_SERVER ); delete_site_transient( \WP_Defender\Component\Security_Tweaks\Servers\Apache::CACHE_APACHE_VERSION ); wp_clear_scheduled_hook( 'wpdef_sec_key_gen' ); } /** * Delete all the data & the cache. */ public function remove_data() { // Remove cached data. Array_Cache::remove( 'tweaks', 'tweaks' ); } /** * Automate the process of resolving, reverting, and ignoring security tweaks based on the provided data. * * @param array $data The data containing information about fixed, issues, and ignored security tweaks. * @param string $request_reason The reason for the request, e.g., 'hub'. * * @return mixed Whether a reauthentication is needed after processing the tweaks. * @since 2.8.1 Add $request_reason param. If there's a request from Hub the plugin doesn't send the error message. */ public function automate( $data, $request_reason ) { $this->refresh_tweaks_status(); $need_reauth = false; // Resolve tweaks. if ( ! empty( $data['fixed'] ) ) { // There are some tweak that need manual apply, as files based, or change admin. $manual_done = array( 'replace-admin-username', 'prevent-php-executed', 'wp-version', 'php-version', 'protect-information', ); if ( 'hub' === $request_reason ) { $manual_done[] = 'security-key'; } $diff_keys = array_diff( $data['fixed'], $this->model->fixed, $manual_done ); if ( ! empty( $diff_keys ) ) { foreach ( $diff_keys as $slug ) { $tweak = $this->get_tweak( $slug ); if ( $tweak->has_method( 'bulk_process' ) ) { $ret = $tweak->bulk_process(); } else { $ret = $tweak->process(); } if ( is_wp_error( $ret ) ) { if ( 'hub' === $request_reason ) { continue; } $data = $tweak->to_array(); return sprintf( /* translators: 1: Security tweak title, 2: Error message */ esc_html__( 'There is an error while processing recommendation %1$s, error message: %2$s', 'defender-security' ), $data['title'], $ret->get_error_message() ); } $this->model->mark( self::STATUS_RESOLVE, $slug ); } if ( in_array( 'security-key', $diff_keys, true ) ) { $need_reauth = true; } } } // Revert tweaks. if ( ! empty( $data['issues'] ) ) { $diff_keys = array_diff( $data['issues'], $this->model->issues ); if ( ! empty( $diff_keys ) ) { // Issues. foreach ( $diff_keys as $slug ) { $tweak = $this->get_tweak( $slug ); $ret = $tweak->revert(); if ( is_wp_error( $ret ) ) { if ( 'hub' === $request_reason ) { continue; } $data = $tweak->to_array(); return sprintf( /* translators: 1: Security tweak title, 2: Error message */ esc_html__( 'There is an error while processing recommendation %1$s, error message: %2$s', 'defender-security' ), $data['title'], $ret->get_error_message() ); } $this->model->mark( self::STATUS_ISSUES, $slug ); } } } // Ignore tweaks. if ( ! empty( $data['ignore'] ) ) { $diff_keys = array_diff( $data['ignore'], $this->model->ignore ); if ( ! empty( $diff_keys ) ) { foreach ( $diff_keys as $slug ) { $this->model->mark( self::STATUS_IGNORE, $slug ); } } } return $need_reauth; } /** * Imports data into the model. * * @param array $data Data to be imported into the model. */ public function import_data( array $data ) { $enabled_user_enums = array(); if ( isset( $data['enabled_user_enums'] ) ) { $enabled_user_enums = (array) $data['enabled_user_enums']; unset( $data['enabled_user_enums'] ); } $this->prevent_enum_users->set_enabled_user_enums( $enabled_user_enums ); if ( ! empty( $data['security_key'] ) && is_array( $data['security_key'] ) ) { $this->security_key->update_all_option( $data['security_key'] ); } $model = new Model_Security_Tweaks(); $model->import( $data ); if ( $model->validate() ) { $model->save(); } } /** * Exports strings. * * @return array An array of strings. */ public function export_strings(): array { $this->refresh_tweaks_status(); $settings = new Model_Security_Tweaks(); $strings = array(); $count_all = count( $settings->fixed ) + count( $settings->issues ) + count( $settings->ignore ); if ( empty( $settings->issues ) ) { $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' ); } else { $strings[] = sprintf( /* translators: 1: Total security tweaks activated, 2: Total security tweaks */ esc_html__( '%1$d/%2$d recommendations activated', 'defender-security' ), count( $settings->fixed ), $count_all ); } $tweak_notification = new Tweak_Reminder(); if ( 'enabled' === $tweak_notification->status ) { $strings[] = esc_html__( 'Email notifications active', 'defender-security' ); } return $strings; } /** * Config strings. * * @param array $config Settings. * @param bool $is_pro True if it is a pro version. * * @return array Strings to be displayed in the settings page. */ public function config_strings( $config, $is_pro ): array { $strings = array(); if ( empty( $config['issues'] ) ) { $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' ); } else { $strings[] = sprintf( /* translators: 1: Total security tweaks activated, 2: Total security tweaks */ esc_html__( '%1$d/%2$d recommendations activated', 'defender-security' ), is_array( $config['fixed'] ) || $config['fixed'] instanceof Countable ? count( $config['fixed'] ) : 0, ( is_array( $config['fixed'] ) || $config['fixed'] instanceof Countable ? count( $config['fixed'] ) : 0 ) + ( is_array( $config['issues'] ) || $config['issues'] instanceof Countable ? count( $config['issues'] ) : 0 ) + ( is_array( $config['ignore'] ) || $config['ignore'] instanceof Countable ? count( $config['ignore'] ) : 0 ) ); } if ( 'enabled' === $config['notification'] ) { $strings[] = esc_html__( 'Email notifications active', 'defender-security' ); } return $strings; } /** * Update auto generate flag. * * @param Request $request Request object. * * @defender_route */ public function update_autogenerate_flag( Request $request ): Response { $data = $request->get_data(); $is_autogen_flag = isset( $data['is_autogenerate_keys'] ) ? filter_var( $data['is_autogenerate_keys'], FILTER_VALIDATE_BOOLEAN ) : false; $is_success = false; $message = esc_html__( 'An error occurred, try again.', 'defender-security' ); if ( $this->security_key->set_is_autogenrate_keys( $is_autogen_flag ) ) { $is_success = true; if ( $is_autogen_flag ) { $this->security_key->cron_schedule(); $message = esc_html__( 'Security key/salt autogenerate enabled.', 'defender-security' ); } else { $this->security_key->cron_unschedule(); $message = esc_html__( 'Security key/salt autogenerate disabled.', 'defender-security' ); } } return new Response( $is_success, array( 'message' => $message ) ); } /** * Get component security key instance. * * @return Security_Key */ public function get_security_key() { return $this->security_key; } /** * Update enabled user enums list. * * @param Request $request Request object. * * @return Response * @defender_route */ public function update_enabled_user_enums( Request $request ): Response { $data = (array) $request->get_data(); $enabled_user_enums = $data['enabled_user_enums']; $is_success = false; $message = esc_html__( 'An error occurred, try again.', 'defender-security' ); if ( $this->prevent_enum_users->set_enabled_user_enums( $enabled_user_enums ) ) { $is_success = true; $message = esc_html__( 'User enumeration option(s) updated successfully.', 'defender-security' ); } return new Response( $is_success, array( 'message' => $message ) ); } /** * Handle tweaks rating notice. * * @param Request $request Request. * * @return Response * @defender_route */ public function handle_notice( Request $request ): Response { update_site_option( Rate::SLUG_FOR_BUTTON_RATE, true ); return new Response( true, array() ); } /** * Attention: Tweaks rating notice doesn't have postpone_notice route. * * @param Request $request Request object. * * @defender_route * @return Response */ public function refuse_notice( Request $request ): Response { update_site_option( Rate::SLUG_FOR_BUTTON_THANKS, true ); return new Response( true, array() ); } /** * Tracks a tweak recommendation. * * @param string $title The title of the tweak recommendation. * @param string $status The status of the tweak recommendation. * * @return void */ private function track_tweak( string $title, string $status ) { if ( ! defender_is_wp_cli() ) { $this->track_feature( 'def_recommendation_applied', array( 'Recommendation Name' => $title, 'Status' => $status, ) ); } } }