File manager - Edit - /home/xfekoga/grenier/wp-content/plugins/defender-security/src/component/ip/class-antibot-global-firewall.php
Back
<?php /** * Responsible for handling AntiBot Global Firewall. * * @package WP_Defender\Component\IP */ namespace WP_Defender\Component\IP; use Exception; use Generator; use WP_Defender\Behavior\WPMUDEV; use WP_Defender\Component; use WP_Defender\Controller\Firewall; use WP_Defender\Integrations\Antibot_Global_Firewall_Client; use WP_Defender\Model\Antibot_Global_Firewall as Antibot_Global_Firewall_Model; use WP_Defender\Model\Setting\Antibot_Global_Firewall_Setting; use WP_Defender\Traits\Defender_Dashboard_Client; use WP_Error; /** * Provides methods for downloading IPs from AntiBot Global Firewall, storing, deleting and checking IPs in DB table. */ class Antibot_Global_Firewall extends Component { use Defender_Dashboard_Client; public const REASON_SLUG = 'antibot_global_firewall'; public const LOG_FILE_NAME = 'antibot-global-firewall.log'; public const DOWNLOAD_SYNC_SCHEDULE = 'twicedaily'; public const DOWNLOAD_SYNC_NEXT_RUN_OPTION = 'wpdef_antibot_global_firewall_download_sync_next_run'; public const LAST_SYNC_OPTION = 'wpdef_antibot_global_firewall_last_sync'; public const NOTICE_SLUG = 'wpdef_show_antibot_global_firewall_notice'; public const BLOCKLIST_STATS_KEY = 'wpdef_antibot_global_firewall_stats'; public const IS_SWITCHING_TO_PLUGIN_IN_PROGRESS = 'wpdef_antibot_global_firewall_switching_to_plugin_in_progress'; /** * The AntiBot Global Firewall model for storing IPs. * * @var Antibot_Global_Firewall_Model */ private $model; /** * The AntiBot Global Firewall setting model. * * @var Antibot_Global_Firewall_Setting */ private $model_setting; /** * The WPMUDEV object. * * @var WPMUDEV */ private $wpmudev; /** * The client object for AntiBot Global Firewall. * * @var Antibot_Global_Firewall_Client */ private $antibot_client; /** * Lock file name for firewall updates. * * @var string */ protected string $lock_filename = 'antibot_global_firewall.lock'; /** * Initializes the class with the Antibot_Global_Firewall_Model, Antibot_Global_Firewall_Setting, WPMUDEV and Antibot_Global_Firewall_Client instances. * * @param Antibot_Global_Firewall_Model $model The AntiBot Global Firewall model. * @param Antibot_Global_Firewall_Setting $model_setting The AntiBot Global FIrewall setting model. * @param WPMUDEV $wpmudev The WPMUDEV object. * @param Antibot_Global_Firewall_Client $antibot_client The client object for AntiBot Global Firewall API. */ public function __construct( Antibot_Global_Firewall_Model $model, Antibot_Global_Firewall_Setting $model_setting, WPMUDEV $wpmudev, Antibot_Global_Firewall_Client $antibot_client ) { $this->model = $model; $this->model_setting = $model_setting; $this->wpmudev = $wpmudev; $this->antibot_client = $antibot_client; add_action( 'wpdef_confirm_antibot_toggle_on_hosting', array( $this, 'confirm_toggle_on_hosting' ) ); } /** * Check if AntiBot Global Firewall feature is enabled. * * @return bool True for enabled or false for disabled. */ public function is_enabled(): bool { $is_enabled = $this->model_setting->enabled; /** * Filter to enable or disable the AntiBot Global Firewall. * * @param bool $is_enabled True for enabled or false for disabled. */ return (bool) apply_filters( 'wpdef_antibot_enabled', $is_enabled ); } /** * Check if the AntiBot Global Firewall is enabled based on managed by. * * @return bool True if the AntiBot Global Firewall is enabled, false otherwise. */ public function frontend_is_enabled(): bool { return 'plugin' === $this->get_managed_by() ? $this->is_enabled() : true === defender_get_hosting_feature_state( 'antibot' ); } /** * Check if the AntiBot Global Firewall is enabled and the site is connected to the HUB. * * @return bool True if the AntiBot Global Firewall is active, false otherwise. */ public function is_active(): bool { return $this->frontend_is_enabled() && $this->is_site_connected_to_hub_via_hcm_or_dash(); } /** * Check if the AntiBot Global Firewall is active via plugin. * * @since 5.1.1 * @return bool True if the AntiBot Global Firewall is active via plugin, false otherwise. */ public function is_active_via_plugin(): bool { return 'plugin' === $this->get_managed_by() && $this->is_enabled() && $this->is_site_connected_to_hub_via_hcm_or_dash(); } /** * Check if the IP is blocked. * * @param string $ip The IP address. * * @return bool True if the IP is blocked, false otherwise. */ public function is_ip_blocked( string $ip ): bool { $result = $this->model->get_by_ip( $ip ); return isset( $result->unlocked ) && 1 !== $result->unlocked; } /** * Delete complete blocklist. */ public function delete_blocklist() { $this->model->truncate(); delete_site_option( self::LAST_SYNC_OPTION ); } /** * Check if the Unlock Me Captcha should be displayed. * * @param array $ips The IP addresses. * * @return bool True if the IP is blocked by AntiBot Global Firewall, false otherwise. */ public function is_displayed( array $ips ): bool { if ( ! $this->is_enabled() ) { return false; } foreach ( $ips as $ip ) { if ( $this->is_ip_blocked( $ip ) ) { return true; } } return false; } /** * Get the Unlock Me Captcha button text. * * @return string The button text. */ public static function get_button_text(): string { return esc_html__( 'AntiBot Unlock Me', 'defender-security' ); } /** * Handle the download and store blocklist. * * @since 4.8.0 * @return void */ public function download_and_store_blocklist(): void { if ( ! $this->is_site_connected_to_hub_via_hcm_or_dash() ) { return; } if ( $this->has_lock() ) { $this->log( 'Fallback as already a process is running', Firewall::FIREWALL_LOG ); return; } $this->create_lock(); $file_path = $this->download_blocklist(); if ( ! empty( $file_path ) ) { $this->store_blocklist( $file_path ); } $this->remove_lock(); } /** * Download blocklist. * * @since 4.8.0 * @return string|void */ private function download_blocklist() { $response = $this->antibot_client->get_blocklist_download(); if ( is_wp_error( $response ) ) { $this->log( sprintf( 'AntiBot Global Firewall Error: %s', $response->get_error_message() ), Firewall::FIREWALL_LOG ); return; } elseif ( isset( $response['status'] ) && 'error' === $response['status'] ) { $this->log( sprintf( 'AntiBot Global Firewall Error: %s', $response['message'] ), Firewall::FIREWALL_LOG ); return; } elseif ( empty( $response['data']['download_url'] ) && empty( $response['data']['hashes']['sha256'] ) ) { $this->log( 'AntiBot Global Firewall Error: Download link not found in the response.', Firewall::FIREWALL_LOG ); return; } $file_url = $response['data']['download_url']; if ( ! function_exists( 'download_url' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $tmp_file = download_url( $file_url ); if ( is_wp_error( $tmp_file ) ) { $this->log( 'AntiBot Global Firewall Error: Unable to download file', Firewall::FIREWALL_LOG ); return; } $expected_file_hash = ''; $response = wp_remote_get( $response['data']['hashes']['sha256'] ); if ( wp_remote_retrieve_response_code( $response ) === 200 ) { $expected_file_hash = trim( wp_remote_retrieve_body( $response ) ); } $file_hash = hash_file( 'sha256', $tmp_file ); if ( $file_hash !== $expected_file_hash ) { $this->log( 'AntiBot Global Firewall Error: File is not legit. Hash does not match.', Firewall::FIREWALL_LOG ); return; } $this->log( 'AntiBot Global Firewall: Downloaded successfully.', Firewall::FIREWALL_LOG ); return $tmp_file; } /** * Store blocklist. * * @param string $file_path File path. * * @since 4.8.0 * @return void */ private function store_blocklist( string $file_path ): void { try { $generator = $this->read_file_generator( $file_path ); $this->delete_blocklist(); $this->model->bulk_insert( $generator ); self::set_last_sync(); $this->log( 'AntiBot Global Firewall: IPs stored in the DB table.', Firewall::FIREWALL_LOG ); } catch ( Exception $e ) { $this->log( 'AntiBot Global Firewall Error: ' . $e->getMessage(), Firewall::FIREWALL_LOG ); } // Remove temporary file. wp_delete_file( $file_path ); } /** * Read file generator. * * @param string $file_path File path. * * @return Generator * @throws Exception If file not found or could not open the file. */ private function read_file_generator( $file_path ) { if ( ! file_exists( $file_path ) ) { throw new Exception( 'File not found.' ); } global $wp_filesystem; $lines = $wp_filesystem->get_contents_array( $file_path ); if ( empty( $lines ) || ! is_array( $lines ) ) { throw new Exception( 'Could not retrieve the file contents!' ); } try { foreach ( $lines as $line ) { yield trim( $line ); } } catch ( Exception $exception ) { throw $exception; } } /** * Get the blocklisted IP count. * * @return string The blocklisted IP count. */ public function get_blocklisted_ip_count(): string { // Check if the feature is enabled. if ( ! $this->frontend_is_enabled() ) { return '0'; } // Since from v5.0.2 one method is used for counting. return number_format( $this->get_cached_blocklisted_ips() ); } /** * Get the last sync timestamp. * * @return false|int The last sync timestamp. */ public static function get_last_sync() { return get_site_option( self::LAST_SYNC_OPTION, false ); } /** * Set the last sync timestamp. */ public static function set_last_sync(): void { update_site_option( self::LAST_SYNC_OPTION, time() ); } /** * Check if the IPs should be downloaded from Antibot service. * * @return bool True if the IPs should be downloaded, false otherwise. */ public function maybe_download(): bool { if ( $this->wpmudev->is_wpmu_hosting() && 'plugin' !== $this->get_managed_by() ) { return false; } $last_sync = self::get_last_sync(); if ( false === $last_sync ) { return true; } return time() - $last_sync >= 12 * HOUR_IN_SECONDS; } /** * Get the default managed by. * * @return string The default managed by. */ public function get_default_managed_by(): string { return $this->wpmudev->is_wpmu_hosting() ? 'hosting' : 'plugin'; } /** * Get the AntiBot managed by. * * @return string The AntiBot managed by. */ public function get_managed_by(): string { $managed_by = $this->model_setting->managed_by; return '' !== $managed_by ? $managed_by : $this->get_default_managed_by(); } /** * Get the AntiBot managed by label. * * @return string The AntiBot managed by label. */ public function get_managed_by_label(): string { return 'plugin' === $this->get_managed_by() ? esc_html__( 'Defender Plugin', 'defender-security' ) : esc_html__( 'WPMU DEV Hosting', 'defender-security' ); } /** * Set the AntiBot managed by. * * @param string $managed_by The managed by value. * * @return bool True if the managed by is set, false otherwise. */ public function set_managed_by( string $managed_by ): bool { if ( in_array( $managed_by, Antibot_Global_Firewall_Setting::MANAGED_BY_ALLOWED, true ) ) { $this->model_setting->managed_by = $managed_by; $this->model_setting->save(); return true; } return false; } /** * Switch the AntiBot managed by. * * @return string|false The managed by value if it's switched, false otherwise. */ public function switch_managed_by() { $managed_by = 'plugin' === $this->get_managed_by() ? 'hosting' : 'plugin'; if ( $this->set_managed_by( $managed_by ) ) { if ( 'plugin' === $managed_by ) { return false === $this->managed_by_plugin_action() ? false : $managed_by; } else { return false === $this->managed_by_hosting_action() ? false : $managed_by; } } return false; } /** * Action when the AntiBot Global Firewall is enabled/disabled by plugin. * * @param bool $enabled True to enable, false to disable. * * @return bool True if the enabled/disabled action is successful, false otherwise. */ public function managed_by_plugin_action( bool $enabled = true ): bool { if ( $enabled ) { set_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS, true, 30 ); // Disable AntiBot on hosting if it's currently enabled. $this->toggle_on_hosting( false, 0.1 ); // Schedule a single event to confirm AntiBot is disabled on hosting after 15 seconds. if ( ! wp_next_scheduled( 'wpdef_confirm_antibot_toggle_on_hosting' ) ) { wp_schedule_single_event( time() + 15, 'wpdef_confirm_antibot_toggle_on_hosting', array( false ) ); } // Enable AntiBot on plugin side. $this->model_setting->enabled = true; $this->model_setting->save(); // Download IPs. $this->download_and_store_blocklist(); } else { // Disable AntiBot on plugin side. $this->model_setting->enabled = false; $this->model_setting->save(); // Remove IPs. $this->delete_blocklist(); } return true; } /** * Action when the AntiBot Global Firewall is enabled/disabled by hosting. * * @param bool $enabled True to enable, false to disable. * @return bool True if the enabled/disabled action is successful, false otherwise. */ public function managed_by_hosting_action( bool $enabled = true ): bool { if ( $enabled ) { wp_clear_scheduled_hook( 'wpdef_confirm_antibot_toggle_on_hosting' ); delete_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS ); // Disable AntiBot on plugin side. $this->model_setting->enabled = false; $this->model_setting->save(); // Remove IPs from DB table. $this->delete_blocklist(); // Enable AntiBot on hosting if it's currently disabled. $result = $this->toggle_on_hosting( true ); if ( is_wp_error( $result ) ) { return false; } } else { $result = $this->toggle_on_hosting( false ); if ( is_wp_error( $result ) ) { return false; } } return true; } /** * Toggle the AntiBot Global Firewall on hosting. * * @param bool $enable True to enable, false to disable. * @param float $timeout The timeout for the request. * * @return bool|WP_Error True if the AntiBot Global Firewall is toggled, false if not, WP_Error otherwise. */ public function toggle_on_hosting( bool $enable, float $timeout = 30 ) { if ( ! $this->wpmudev->is_wpmu_hosting() ) { return false; } $hosting_enable = defender_get_hosting_feature_state( 'antibot' ); if ( ( $enable && ! $hosting_enable ) || ( ! $enable && $hosting_enable ) ) { $this->attach_behavior( WPMUDEV::class, WPMUDEV::class ); $data = $this->make_wpmu_request( WPMUDEV::API_ANTIBOT_GLOBAL_FIREWALL, array( 'is_active' => $enable ), array( 'method' => 'PUT', 'timeout' => $timeout, ) ); if ( $timeout > 1 && is_wp_error( $data ) ) { $this->log( 'AntiBot Global Firewall Error: ' . $data->get_error_message(), Firewall::FIREWALL_LOG ); return $data; } } return true; } /** * Fetches the number of blocklisted IPs from cache or from the Blocklist API. * * If the value is not cached, it will fetch the number of blocklisted IPs from the Blocklist API and cache it. * * @return int The number of blocklisted IPs. */ public function get_cached_blocklisted_ips(): int { $cached_data = get_site_transient( self::BLOCKLIST_STATS_KEY ); if ( false !== $cached_data ) { return (int) $cached_data; } $blocklisted_ips = $this->antibot_client->get_blocklist_stats(); if ( is_wp_error( $blocklisted_ips ) ) { $this->log( 'AntiBot Global Firewall Error: ' . $blocklisted_ips->get_error_message(), Firewall::FIREWALL_LOG ); return 0; } set_site_transient( self::BLOCKLIST_STATS_KEY, $blocklisted_ips, 12 * HOUR_IN_SECONDS ); return $blocklisted_ips; } /** * Confirm the toggle on hosting. * * @param bool $enable True to enable, false to disable. * * @return void */ public function confirm_toggle_on_hosting( bool $enable ): void { $this->toggle_on_hosting( $enable ); delete_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS ); } /** * Logs IP-related messages if logging is enabled via filter. * * @param string $message The message to be logged. */ public function log_ip_message( string $message ): void { /** * Filters whether IP logging is enabled. * * This filter allows developers to enable or disable IP logging globally. * Returning false will prevent the log message from being written. * * @param bool $is_enabled Whether IP logging is enabled. Default true. * @since 5.1.0 */ $is_logging_enabled = (bool) apply_filters( 'wpdef_antibot_global_firewall_ip_log', true ); if ( ! $is_logging_enabled ) { return; } $this->log( $message, self::LOG_FILE_NAME ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.0.30 | Generation time: 0 |
proxy
|
phpinfo
|
Settings