plugin_instance = ADVAIPBL_Main::get_instance(); $this->session_manager = $this->plugin_instance->session_manager; if ( function_exists( 'switch_to_locale' ) ) { switch_to_locale( 'en_US' ); } } /** * Manually blocks an IP address. * * ## OPTIONS * * * : The IP address to block. * * [--reason=] * : An optional reason for the block. * --- * default: Blocked via WP-CLI * --- * * ## EXAMPLES * * wp advaipbl block 1.2.3.4 * wp advaipbl block 1.2.3.4 --reason="Suspicious activity from logs" */ public function block( $args, $assoc_args ) { list( $ip ) = $args; $reason = $assoc_args['reason'] ?? 'Blocked via WP-CLI'; if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { WP_CLI::error( sprintf( "'%s' is not a valid IP address.", $ip ) ); } $list = get_option( 'advaipbl_ips_bloqueadas_manual', [] ); if ( array_key_exists( $ip, $list ) ) { WP_CLI::warning( sprintf( 'IP %s was already blocked.', $ip ) ); return; } $list[ $ip ] = [ 'timestamp' => time(), 'detail' => $reason, ]; update_option( 'advaipbl_ips_bloqueadas_manual', $list ); set_transient( 'advaipbl_bloqueo_manual_' . md5( $ip ), true, 10 * YEAR_IN_SECONDS ); $this->plugin_instance->log_event( sprintf( 'WP-CLI: IP %s manually blocked.', $ip ), 'warning' ); WP_CLI::success( sprintf( 'IP %s blocked successfully.', $ip ) ); } /** * Unblocks an IP address from all blocklists. * * ## OPTIONS * * * : The IP address to unblock. */ public function unblock( $args ) { list( $ip ) = $args; if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { WP_CLI::error( sprintf( "'%s' is not a valid IP address.", $ip ) ); } $this->plugin_instance->desbloquear_ip( $ip ); $this->plugin_instance->log_event( sprintf( 'WP-CLI: IP %s unblocked from all lists.', $ip ), 'info' ); WP_CLI::success( sprintf( 'IP %s unblocked from all lists.', $ip ) ); } /** * Displays a list of currently blocked IPs. * * ## OPTIONS * * [--type=] * : Filter the list by block type. * --- * default: all * options: * - all * - manual * - honeypot * - user_agent * - 404 * - 403 * - login * --- * * [--format=] * : Render output in a particular format. * --- * default: table * options: * - table * - json * - csv * - yaml * --- */ public function blocked( $args, $assoc_args ) { $all_blocked_ips = $this->plugin_instance->get_all_blocked_ips_for_cli(); $filter_type = $assoc_args['type'] ?? 'all'; if ( 'all' !== $filter_type ) { $all_blocked_ips = array_filter( $all_blocked_ips, function ( $block ) use ( $filter_type ) { return $block['type'] === $filter_type; } ); } if ( empty( $all_blocked_ips ) ) { WP_CLI::line( 'No blocked IPs found for the selected filter.' ); return; } WP_CLI\Utils\format_items( $assoc_args['format'] ?? 'table', array_values( $all_blocked_ips ), [ 'ip', 'type_label', 'timestamp', 'detail' ] ); } /** * Manages active user sessions. * * @subcommand session */ public function session( $args, $assoc_args ) { $subcommand = array_shift( $args ); switch ( $subcommand ) { case 'list': $this->session_list( $args, $assoc_args ); break; case 'terminate': $this->session_terminate( $args, $assoc_args ); break; default: WP_CLI::error( "Please specify a subcommand for 'session': list or terminate." ); } } /** * Lists active user sessions. * * @alias list */ private function session_list( $args, $assoc_args ) { $sessions = $this->session_manager->get_active_sessions(); $role_filter = $assoc_args['role'] ?? null; if ( $role_filter ) { $sessions = array_filter($sessions, function($session) use ($role_filter) { return in_array($role_filter, explode(', ', $session['role']), true); }); } if (empty($sessions)) { WP_CLI::line('No active user sessions found.'); return; } WP_CLI\Utils\format_items($assoc_args['format'] ?? 'table', $sessions, ['user_id', 'username', 'role', 'ip', 'last_activity']); } /** * Terminates active user sessions. * * @alias terminate */ private function session_terminate( $args, $assoc_args ) { if (empty($assoc_args) || (!isset($assoc_args['user-id']) && !isset($assoc_args['user-login']) && !isset($assoc_args['role']) && !isset($assoc_args['all']))) { WP_CLI::error('Please specify a condition to terminate sessions (e.g., --user-id, --role, --all).'); } $user_ids_to_logout = []; $log_message = ''; if ( isset( $assoc_args['user-id'] ) ) { $user_id = absint($assoc_args['user-id']); if ( get_userdata($user_id) ) { $user_ids_to_logout[] = $user_id; $log_message = sprintf('WP-CLI: Sessions for user ID %d terminated.', $user_id); } else { WP_CLI::error(sprintf('User with ID %d not found.', $user_id)); } } elseif ( isset( $assoc_args['user-login'] ) ) { $user = get_user_by('login', $assoc_args['user-login']); if ($user) { $user_ids_to_logout[] = $user->ID; $log_message = sprintf('WP-CLI: Sessions for user "%s" terminated.', $assoc_args['user-login']); } else { WP_CLI::error(sprintf('User with login "%s" not found.', $assoc_args['user-login'])); } } elseif ( isset( $assoc_args['role'] ) ) { $role = sanitize_text_field($assoc_args['role']); $user_ids_to_logout = get_users(['role' => $role, 'fields' => 'ID']); $log_message = sprintf('WP-CLI: All sessions for role "%s" terminated.', $role); } elseif ( isset( $assoc_args['all'] ) ) { $user_ids_to_logout = get_users(['fields' => 'ID']); $log_message = 'WP-CLI: All sessions for all users terminated.'; } if (empty($user_ids_to_logout)) { WP_CLI::warning('No users found matching the criteria. No sessions terminated.'); return; } WP_CLI::confirm(sprintf('This will terminate sessions for %d user(s). Are you sure?', count($user_ids_to_logout)), $assoc_args); $terminated_count = 0; foreach ($user_ids_to_logout as $user_id) { WP_Session_Tokens::get_instance($user_id)->destroy_all(); $terminated_count++; } $this->plugin_instance->log_event($log_message, 'warning'); WP_CLI::success(sprintf('Successfully terminated sessions for %d user(s).', $terminated_count)); } /** * Manages the IP whitelist. * * @subcommand whitelist */ public function whitelist( $args, $assoc_args ) { $subcommand = array_shift( $args ); switch ( $subcommand ) { case 'add': $this->whitelist_add( $args, $assoc_args ); break; case 'remove': $this->whitelist_remove( $args, $assoc_args ); break; case 'list': $this->whitelist_list( $args, $assoc_args ); break; default: WP_CLI::error( "Please specify a subcommand for 'whitelist': add, remove, or list." ); } } private function whitelist_add( $args, $assoc_args ) { list( $ip ) = $args; if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { WP_CLI::error( sprintf( "'%s' is not a valid IP address.", $ip ) ); } $list = get_option( 'advaipbl_ips_whitelist', [] ); if ( array_key_exists( $ip, $list ) ) { WP_CLI::warning( sprintf( 'IP %s was already in the whitelist.', $ip ) ); return; } $list[ $ip ] = time(); update_option( 'advaipbl_ips_whitelist', $list ); $this->plugin_instance->desbloquear_ip( $ip ); $this->plugin_instance->log_event( sprintf( 'WP-CLI: IP %s added to whitelist.', $ip ) ); WP_CLI::success( sprintf( 'IP %s added to whitelist.', $ip ) ); } private function whitelist_remove( $args, $assoc_args ) { list( $ip ) = $args; if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { WP_CLI::error( sprintf( "'%s' is not a valid IP address.", $ip ) ); } $list = get_option( 'advaipbl_ips_whitelist', [] ); if ( ! array_key_exists( $ip, $list ) ) { WP_CLI::warning( sprintf( 'IP %s was not in the whitelist.', $ip ) ); return; } unset( $list[ $ip ] ); update_option( 'advaipbl_ips_whitelist', $list ); $this->plugin_instance->log_event( sprintf( 'WP-CLI: IP %s removed from whitelist.', $ip ) ); WP_CLI::success( sprintf( 'IP %s removed from whitelist.', $ip ) ); } private function whitelist_list( $args, $assoc_args ) { $whitelist = get_option( 'advaipbl_ips_whitelist', [] ); if ( empty( $whitelist ) ) { WP_CLI::line( 'The whitelist is empty.' ); return; } $items = []; foreach ( $whitelist as $ip => $timestamp ) { $items[] = [ 'ip' => $ip, 'added_since' => ADVAIPBL_Main::get_formatted_datetime( $timestamp ), ]; } WP_CLI\Utils\format_items( $assoc_args['format'] ?? 'table', $items, [ 'ip', 'added_since' ] ); } /** * Manages the plugin's logs. * * @subcommand log */ public function log( $args, $assoc_args ) { $subcommand = array_shift( $args ); switch ( $subcommand ) { case 'list': $this->log_list( $args, $assoc_args ); break; case 'clear': $this->log_clear( $args, $assoc_args ); break; default: WP_CLI::error( "Please specify a subcommand for 'log': list or clear." ); } } private function log_list( $args, $assoc_args ) { global $wpdb; $table_name = $wpdb->prefix . 'advaipbl_logs'; $type = $assoc_args['type'] ?? 'all'; $count = isset( $assoc_args['count'] ) ? absint( $assoc_args['count'] ) : 20; $where_clause = '1=1'; if ( in_array( $type, [ 'general', '404', '403', 'login', 'honeypot', 'user_agent' ], true ) ) { $where_clause = $wpdb->prepare( 'log_type = %s', $type ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery $logs = $wpdb->get_results( $wpdb->prepare( "SELECT timestamp, level, ip, message, details FROM $table_name WHERE $where_clause ORDER BY timestamp DESC LIMIT %d", $count ), ARRAY_A ); if ( empty( $logs ) ) { WP_CLI::line( 'No log entries found for the specified type.' ); return; } WP_CLI\Utils\format_items( $assoc_args['format'] ?? 'table', $logs, [ 'timestamp', 'level', 'ip', 'message' ] ); } private function log_clear( $args, $assoc_args ) { WP_CLI::confirm( 'Are you sure you want to clear these logs? This action cannot be undone.', $assoc_args ); global $wpdb; $table_name = $wpdb->prefix . 'advaipbl_logs'; $type = $assoc_args['type'] ?? 'all'; $this->plugin_instance->log_event(sprintf('WP-CLI: Clearing logs of type "%s".', $type), 'warning'); if ( in_array( $type, [ 'general', '404', '403', 'login', 'honeypot', 'user_agent' ], true ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $deleted = $wpdb->delete( $table_name, [ 'log_type' => $type ], [ '%s' ] ); WP_CLI::success( sprintf( "%d logs of type '%s' have been cleared.", $deleted, $type ) ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query( "TRUNCATE TABLE $table_name" ); WP_CLI::success( 'All logs have been cleared.' ); } } /** * Gets or sets configuration options. * * @subcommand config */ public function config( $args, $assoc_args ) { $subcommand = array_shift( $args ); switch ( $subcommand ) { case 'get': $this->config_get( $args, $assoc_args ); break; case 'set': $this->config_set( $args, $assoc_args ); break; default: WP_CLI::error( "Please specify a subcommand for 'config': get or set." ); } } private function config_get( $args, $assoc_args ) { $options = get_option( 'advaipbl_settings', [] ); $key = array_shift( $args ); if ( $key ) { if ( isset( $options[ $key ] ) ) { WP_CLI::line( is_array($options[$key]) ? wp_json_encode($options[$key]) : $options[ $key ] ); } else { WP_CLI::error( sprintf( "Option '%s' not found.", $key ) ); } } else { $display_items = []; foreach ($options as $option_key => $option_value) { $display_items[] = [ 'key' => $option_key, 'value' => is_array($option_value) ? wp_json_encode($option_value) : $option_value, ]; } WP_CLI\Utils\format_items( 'table', $display_items, [ 'key', 'value' ] ); } } private function config_set( $args, $assoc_args ) { // Verificamos que tenemos exactamente dos argumentos antes de continuar. if ( count( $args ) < 2 ) { WP_CLI::error( 'Usage: wp advaipbl config set ' ); return; } $options = get_option( 'advaipbl_settings', [] ); list($key, $value) = $args; if ( ! array_key_exists( $key, $options ) ) { WP_CLI::error( sprintf( "Option '%s' is not a valid option key. Use 'wp advaipbl config get' to see all keys.", $key ) ); return; } // Saneamos el valor antes de guardarlo. $sanitized_value = sanitize_text_field( $value ); $options[ $key ] = $sanitized_value; update_option( 'advaipbl_settings', $options ); $this->plugin_instance->log_event( sprintf( 'WP-CLI: Option "%s" updated.', $key ), 'info' ); WP_CLI::success( sprintf( "Option '%s' updated to '%s'.", $key, $sanitized_value ) ); } }