$message, 'malicious_bot_message' => $message, 'fake_bots_message' => $message, 'whitelist' => $whitelist, // Blocked User Agents. 'blacklist' => '', 'blocklist_presets' => true, 'blocklist_preset_values' => array( 'mj12bot', 'dotbot' ), 'script_presets' => false, 'script_preset_values' => array(), ); } /** * Initializes the object by setting default values for the message, whitelist, and blacklist properties. * This function retrieves the default values from the get_default_values() method and assigns them to the * corresponding properties of the object. * * @return void */ protected function before_load(): void { $default_values = $this->get_default_values(); $this->message = $default_values['message']; $this->whitelist = $default_values['whitelist']; $this->blacklist = $default_values['blacklist']; $this->blocklist_presets = $default_values['blocklist_presets']; $this->blocklist_preset_values = $default_values['blocklist_preset_values']; $this->script_presets = $default_values['script_presets']; $this->script_preset_values = $default_values['script_preset_values']; $this->malicious_bot_message = $default_values['malicious_bot_message']; $this->fake_bots_message = $default_values['fake_bots_message']; } /** * Checks if the User Agent Lockout feature is active. * * @return bool Returns true if the User Agent Lockout feature is active, false otherwise. */ public function is_active(): bool { return (bool) apply_filters( 'wd_user_agents_enable', $this->enabled ); } /** * Get list of blocklisted or allowlisted data. * * @param string $type blocklist|allowlist. * @param bool $lower Whether to convert the list to lowercase. * * @return array */ public function get_lockout_list( $type = 'blocklist', $lower = true ): array { $data = 'blocklist' === $type ? $this->blacklist : $this->whitelist; $arr = array_filter( preg_split( "/\r\n|\n|\r/", $data ), 'boolval' ); $arr = array_map( 'trim', $arr ); if ( $lower ) { $arr = array_map( 'strtolower', $arr ); } return $arr; } /** * Define settings labels. * * @return array */ public function labels(): array { return array( 'enabled' => self::get_module_name(), 'message' => esc_html__( 'Message', 'defender-security' ), 'blacklist' => esc_html__( 'Blocklist', 'defender-security' ), 'whitelist' => esc_html__( 'Allowlist', 'defender-security' ), 'empty_headers' => esc_html__( 'Empty Headers', 'defender-security' ), ); } /** * Get the access status of this UA. * * @param string $ua User Agent. * * @return array */ public function get_access_status( $ua ): array { $blocklist = str_replace( '#', '\#', $this->get_all_selected_blocklist_ua() ); $allowlist = str_replace( '#', '\#', $this->get_lockout_list( 'allowlist' ) ); // Escape regex special characters in each item before building the pattern. $blocklist_escaped = array_map( 'preg_quote', $blocklist, array_fill( 0, count( $blocklist ), '#' ) ); $allowlist_escaped = array_map( 'preg_quote', $allowlist, array_fill( 0, count( $allowlist ), '#' ) ); $blocklist_regex_pattern = '#' . implode( '|', $blocklist_escaped ) . '#i'; $allowlist_regex_pattern = '#' . implode( '|', $allowlist_escaped ) . '#i'; $blocklist_match = preg_match( $blocklist_regex_pattern, $ua ); $allowlist_match = preg_match( $allowlist_regex_pattern, $ua ); if ( 1 !== $blocklist_match && 1 !== $allowlist_match ) { return array( 'na' ); } $result = array(); // Check blocklist first - if it matches, add 'banned'. if ( 1 === $blocklist_match ) { $result[] = 'banned'; } // Check allowlist - if it matches, add 'allowlist'. if ( 1 === $allowlist_match ) { $result[] = 'allowlist'; } return $result; } /** * Checks if a user agent is present in a given list. * * @param string $ua The user agent to check. * @param string $collection The type of list to check against ('blocklist' or 'allowlist'). * * @return bool Returns true if the user agent is in the list, false otherwise. */ public function is_ua_in_list( $ua, $collection ): bool { if ( 'allowlist' === $collection ) { $arr = str_replace( '#', '\#', $this->get_lockout_list( $collection ) ); } else { $arr = str_replace( '#', '\#', $this->get_all_selected_blocklist_ua() ); } // Escape regex special characters in each item before building the pattern. $arr_escaped = array_map( 'preg_quote', $arr, array_fill( 0, count( $arr ), '#' ) ); $list_regex_pattern = '#' . implode( '|', $arr_escaped ) . '#i'; return 1 === preg_match( $list_regex_pattern, $ua ); } /** * Remove User Agent from a list. * * @param string $ua The user agent to remove. * @param string $collection blocklist|allowlist. * * @return void */ public function remove_from_list( $ua, $collection ) { if ( 'allowlist' === $collection ) { $arr = $this->get_lockout_list( $collection ); // Array can contain uppercase. $orig_arr = str_replace( '#', '\#', $this->get_lockout_list( $collection, false ) ); } else { $arr = $this->get_all_selected_blocklist_ua(); // Array can contain uppercase. $orig_arr = str_replace( '#', '\#', $this->get_all_selected_blocklist_ua( false ) ); } $list_regex_pattern = '#' . implode( '|', $arr ) . '#i'; $list_match = preg_match( $list_regex_pattern, $ua ); if ( false !== $list_match ) { if ( 'blocklist' === $collection ) { // Check in 'Blocklist Presets'. if ( $this->blocklist_presets ) { $key = array_search( $ua, $this->blocklist_preset_values, true ); if ( false !== $key ) { unset( $this->blocklist_preset_values[ $key ] ); $this->blocklist_preset_values = array_values( $this->blocklist_preset_values ); } } // Check in 'Script Presets' using Regex. if ( $this->script_presets ) { if ( false !== strpos( $ua, User_Agent_Service::GO_HTTP_CLIENT_KEY . '/' ) ) { $key_script_preset = User_Agent_Service::GO_HTTP_CLIENT_KEY; } elseif ( false !== strpos( $ua, User_Agent_Service::PYTHON_REQUESTS_KEY . '/' ) ) { $key_script_preset = User_Agent_Service::PYTHON_REQUESTS_KEY; } else { $key_script_preset = ''; } $key = array_search( $key_script_preset, $this->script_preset_values, true ); if ( false !== $key ) { unset( $this->script_preset_values[ $key ] ); $this->script_preset_values = array_values( $this->script_preset_values ); } } // Check 'Custom User Agents' case. $arr_blocklist = $this->get_lockout_list( 'blocklist', false ); if ( array() !== $arr_blocklist ) { $key = array_search( $ua, $arr_blocklist, true ); if ( false !== $key && isset( $arr_blocklist[ $key ] ) ) { unset( $arr_blocklist[ $key ] ); // Convert back to string. $this->blacklist = implode( PHP_EOL, $arr_blocklist ); } } } elseif ( 'allowlist' === $collection ) { $key = array_search( $ua, $arr, true ); if ( false !== $key && isset( $arr[ $key ] ) ) { unset( $arr[ $key ] ); // Convert back to string. $this->whitelist = implode( PHP_EOL, $arr ); } } $this->save(); } } /** * Add an UA to the list. * * @param string $ua User agent name. * @param string $collection blocklist|allowlist. * * @return void */ public function add_to_list( $ua, $collection ) { if ( 'blocklist' === $collection ) { if ( User_Agent_Service::is_blocklist_presets( $ua ) ) { $this->blocklist_presets = true; $this->blocklist_preset_values[] = $ua; } elseif ( User_Agent_Service::is_script_presets( $ua ) ) { $this->script_presets = true; $this->script_preset_values[] = $ua; } else { $this->blacklist = $this->push_ua_to_list( $ua, $collection ); } } elseif ( 'allowlist' === $collection ) { $this->whitelist = $this->push_ua_to_list( $ua, $collection ); } $this->save(); } /** * Push the UA to either blocklist (only in 'Custom User Agents') or allowlist * * @param string $ua User agent name. * @param string $collection List type i.e. blocklist or allowlist. * * @return string List as string format with UA delimited with newline character. */ private function push_ua_to_list( string $ua, string $collection ): string { $arr = $this->get_lockout_list( $collection, false ); $arr[] = trim( $ua ); $arr = array_unique( $arr ); return implode( PHP_EOL, $arr ); } /** * Get the module name. * * @return string */ public static function get_module_name(): string { return esc_html__( 'User Agent Banning', 'defender-security' ); } /** * Get the module state. * * @param bool $flag Module state. * * @return string */ public static function get_module_state( $flag ): string { return $flag ? esc_html__( 'active', 'defender-security' ) : esc_html__( 'inactive', 'defender-security' ); } /** * Get the list of all selected blocklisted UA values. * * @param bool $lower Whether to convert the list to lowercase. * * @return array */ public function get_all_selected_blocklist_ua( $lower = true ): array { $blocklist_custom = $this->get_lockout_list( 'blocklist', $lower ); $blocklist_presets = $this->blocklist_presets ? $this->blocklist_preset_values : array(); $script_presets = $this->script_presets ? $this->script_preset_values : array(); return array_merge( $blocklist_custom, $blocklist_presets, $script_presets ); } /** * Return the module slug. * * @return string */ public static function get_module_slug(): string { return 'ua-lockout'; } }