PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/controller/class-antibot-global-firewall.php +183 -85 5.3.1 → 6.3.0 View file →
@@ -8,8 +8,9 @@
8 8 namespace WP_Defender\Controller;
9 9
10 10 use Calotes\Component\Request;
11 11 use Calotes\Component\Response;
12 +use WP_Defender\Component\Network_Cron_Manager;
12 13 use WP_Defender\Component\Config\Config_Hub_Helper;
13 14 use WP_Defender\Event;
14 15 use WP_Defender\Traits\Setting;
15 16 use WP_Defender\Traits\Defender_Dashboard_Client;
@@ -70,8 +71,10 @@
70 71 $this->model = $model;
71 72 $this->service = $service;
72 73 $this->wpmudev = $wpmudev;
73 74
75 + add_action( 'admin_init', array( $this, 'maybe_hcm_connection_attempt' ), 20 );
76 + add_action( 'wpdef_hub_connector_synced', array( $this, 'maybe_hcm_connection_attempt' ) );
74 77 add_action( 'wpmudev_hub_connector_first_sync_completed', array( $this, 'maybe_hcm_connection_attempt' ) );
75 78
76 79 /**
77 80 * Download and store Blocklist from the API.
@@ -76,19 +79,28 @@
76 79 /**
77 80 * Download and store Blocklist from the API.
78 81 */
79 82 if ( $this->service->is_active_via_plugin() ) {
80 - if ( ! wp_next_scheduled( 'wpdef_antibot_global_firewall_fetch_blocklist' ) ) {
81 - wp_schedule_event( time() + 15, Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_SCHEDULE, 'wpdef_antibot_global_firewall_fetch_blocklist' );
82 - }
83 + /**
84 + * Network Cron Manager
85 + *
86 + * @var Network_Cron_Manager $network_cron_manager
87 + */
88 + $network_cron_manager = wd_di()->get( Network_Cron_Manager::class );
89 + $network_cron_manager->register_callback(
90 + 'wpdef_antibot_global_firewall_fetch_blocklist',
91 + array( $this, 'handle_download_and_store_blocklist' ),
92 + 12 * HOUR_IN_SECONDS,
93 + time() + 15
94 + );
83 95 } elseif ( wp_next_scheduled( 'wpdef_antibot_global_firewall_fetch_blocklist' ) ) {
84 96 wp_clear_scheduled_hook( 'wpdef_antibot_global_firewall_fetch_blocklist' );
85 97 }
86 - add_action( 'wpdef_antibot_global_firewall_fetch_blocklist', array( $this, 'handle_download_and_store_blocklist' ) );
87 98
88 99 if ( $this->wpmudev->is_wpmu_hosting() ) {
89 100 add_action( 'init', array( $this, 'sync_state' ) );
90 101 }
102 + add_action( 'init', array( $this, 'handle_expired_membership' ) );
91 103 }
92 104
93 105 /**
94 106 * Handle request to save settings.
@@ -100,28 +112,29 @@
100 112 */
101 113 public function save_settings( Request $request ) {
102 114 $data = $request->get_data(
103 115 array(
104 - 'enabled' => array( 'type' => 'bool' ),
105 - 'managed_by' => array( 'type' => 'string' ),
116 + 'enabled' => array( 'type' => 'bool' ),
117 + 'managed_by' => array( 'type' => 'string' ),
106 118 // Temporary property.
107 - 'module_title' => array(
119 + 'module_title' => array(
108 120 'type' => 'string',
109 121 'sanitize' => 'sanitize_text_field',
110 122 ),
111 - 'module_location' => array(
123 + 'module_location' => array(
112 124 'type' => 'string',
113 125 'sanitize' => 'sanitize_text_field',
114 126 ),
127 + 'redirect_to_feature_page' => array( 'type' => 'bool' ),
115 128 // End.
116 129 )
117 130 );
118 - $old_enabled = (bool) $this->model->enabled;
131 + $old_enabled = $this->model->enabled;
119 132
120 133 $location = 'Feature Page';
121 134 // Split module's titles and locations.
122 135 if ( isset( $data['module_title'] ) && 'antibot' === $data['module_title'] ) {
123 - if ( empty( $data['module_location'] ) ) {
136 + if ( ! isset( $data['module_location'] ) || '' === $data['module_location'] ) {
124 137 $location = 'Dashboard';
125 138 } else {
126 139 $location = $data['module_location'];
127 140 unset( $data['module_location'] );
@@ -153,13 +166,8 @@
153 166 }
154 167 }
155 168
156 169 Config_Hub_Helper::set_clear_active_flag();
157 -
158 - // Hide the Antibot notice on the Dashboard page.
159 - if ( ! empty( $data['enabled'] ) ) {
160 - delete_site_option( Antibot_Global_Firewall_Component::NOTICE_SLUG );
161 - }
162 170 // Maybe track.
163 171 if ( ! defender_is_wp_cli() && $old_enabled !== $data['enabled'] ) {
164 172 wd_di()->get( Antibot_Analytics::class )->track_antibot( $old_enabled, $location );
165 173 }
@@ -167,13 +175,20 @@
167 175 $update_message = $this->get_update_message( $data, $old_enabled, Antibot_Global_Firewall_Setting::get_module_name() );
168 176 $referrer = wp_get_referer();
169 177 if ( $referrer && strpos( $referrer, 'page=wp-defender' ) !== false ) {
170 178 $update_message = sprintf(
171 - /* translators: 1: Bold open tag, 2: Bold close tag */
179 + /* translators: 1: Module name. */
172 180 __( '%s is now enabled.', 'defender-security' ),
173 181 '<strong>' . Antibot_Global_Firewall_Setting::get_module_name() . '</strong>'
174 182 );
175 183 }
184 +
185 + $data_frontend = $this->data_frontend();
186 +
187 + if ( isset( $data['redirect_to_feature_page'] ) && $data['redirect_to_feature_page'] ) {
188 + $data_frontend['redirect'] = network_admin_url( 'admin.php?page=wdf-ip-lockout' );
189 + }
190 +
176 191 return new Response(
177 192 true,
178 193 array_merge(
179 194 array(
@@ -179,9 +194,9 @@
179 194 array(
180 195 'message' => $update_message,
181 196 'auto_close' => true,
182 197 ),
183 - $this->data_frontend()
198 + $data_frontend
184 199 )
185 200 );
186 201 }
187 202
@@ -191,27 +206,15 @@
191 206 );
192 207 }
193 208
194 209 /**
195 - * Hide the Antibot notice.
196 - *
197 - * @return Response
198 - * @defender_route
199 - */
200 - public function hide_antibot_notice(): Response {
201 - delete_site_option( Antibot_Global_Firewall_Component::NOTICE_SLUG );
202 -
203 - return new Response( true, array() );
204 - }
205 -
206 - /**
207 210 * Queue assets and require data.
208 211 *
209 212 * @return void
210 213 */
211 214 public function enqueue_assets() {
212 - if ( $this->is_page_active() ) {
213 - wp_localize_script( 'def-iplockout', 'antibot', $this->data_frontend() );
215 + if ( ! $this->is_page_active() ) {
216 + return;
214 217 }
215 218 }
216 219
217 220 /**
@@ -219,22 +222,8 @@
219 222 *
220 223 * @return array
221 224 */
222 225 public function data_frontend(): array {
223 - /**
224 - * Show the onboarding reminder notice if:
225 - * 1. If the reminder time is set and the time difference is greater than a week.
226 - * 2. No click on the Cross icon before.
227 - */
228 - $is_reminder = false;
229 - $last_reminder = get_site_option( Onboard::REMINDER_KEY, 0 );
230 - if ( ! empty( $last_reminder ) ) {
231 - $time_diff = time() - $last_reminder;
232 - if ( $time_diff > WEEK_IN_SECONDS ) {
233 - $is_reminder = true;
234 - }
235 - }
236 -
237 226 $model_export = $this->model->export();
238 227 $model_export['managed_by'] = $this->service->get_managed_by();
239 228 $module_name = Antibot_Global_Firewall_Setting::get_module_name();
240 229
@@ -243,15 +232,19 @@
243 232 'model' => $model_export,
244 233 'misc' => array(
245 234 'module_slug' => Antibot_Global_Firewall_Setting::get_module_slug(),
246 235 'module_name' => $module_name,
247 - 'show_notice' => $is_reminder
248 - && (bool) get_site_option( Antibot_Global_Firewall_Component::NOTICE_SLUG, false ),
236 + 'show_notice' => false,
249 237 'sync_schedule' => __( 'Twice Daily', 'defender-security' ),
250 238 'ips_count' => $this->service->get_blocklisted_ip_count(),
239 + 'blocklist_stats_full' => $this->service->get_cached_blocklist_stats_data(),
251 240 'frontend_is_enabled' => $this->service->frontend_is_enabled(),
252 241 'frontend_mode' => $this->service->frontend_mode(),
253 242 'is_active' => $this->service->is_active(),
243 + 'show_stats_button' => ! $this->wpmudev->is_whitelabel_enabled(),
244 + 'show_checker' => ! $this->wpmudev->is_wpmu_hosting()
245 + || $this->wpmudev->is_wpmu_dev_admin()
246 + || ! ( $this->service->is_active_via_hosting() && $this->wpmudev->is_whitelabel_enabled() ),
254 247 'active_tooltip_text' => __( 'List of exploit attempts detected and blocked across all connected sites by AntiBot Firewall.', 'defender-security' ),
255 248 'inactive_tooltip_text' => sprintf(
256 249 /* translators: %s: Module name. */
257 250 __( '%s is Inactive.', 'defender-security' ),
@@ -256,8 +249,11 @@
256 249 /* translators: %s: Module name. */
257 250 __( '%s is Inactive.', 'defender-security' ),
258 251 $module_name
259 252 ),
253 + 'current_user' => esc_html( wp_get_current_user()->display_name ?? __( 'User', 'defender-security' ) ),
254 + 'is_expired_membership' => $this->is_expired_membership_type(),
255 + 'show_antibot_options' => $this->service->get_states_of_antibot_options_for_different_hosting_types(),
260 256 ),
261 257 ),
262 258 $this->dump_routes_and_nonces()
263 259 );
@@ -269,15 +265,15 @@
269 265 * @return void
270 266 */
271 267 public function handle_download_and_store_blocklist(): void {
272 268 if ( is_multisite() ) {
273 - $next_run = get_site_option( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_NEXT_RUN_OPTION, 0 );
274 - if ( ! empty( $next_run ) && $next_run > time() ) {
269 + $next_run = (int) get_site_option( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_NEXT_RUN_OPTION, 0 );
270 + if ( $next_run > 0 && $next_run > time() ) {
275 271 return;
276 272 }
277 273
278 - $interval = wd_di()->get( Scheduler::class )->get_cron_schedule_interval( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_SCHEDULE );
279 - $next_run = time() + ( ! empty( $interval ) ? $interval : 12 * HOUR_IN_SECONDS );
274 + $interval = (int) wd_di()->get( Scheduler::class )->get_cron_schedule_interval( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_SCHEDULE );
275 + $next_run = time() + ( $interval > 0 ? $interval : 12 * HOUR_IN_SECONDS );
280 276 update_site_option( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_NEXT_RUN_OPTION, $next_run );
281 277 }
282 278
283 279 if ( true === $this->service->is_active_via_plugin() ) {
@@ -302,9 +298,9 @@
302 298 public function maybe_hcm_connection_attempt() {
303 299 $data = get_site_transient( Hub_Connector::TRANSIENT_KEY );
304 300 $module_slug = $data['module_slug'] ?? '';
305 301
306 - if ( Antibot_Global_Firewall_Setting::get_module_slug() === $module_slug && self::get_status() ) {
302 + if ( Antibot_Global_Firewall_Setting::get_module_slug() === $module_slug && self::get_hcm_status() ) {
307 303 delete_site_transient( Hub_Connector::TRANSIENT_KEY );
308 304
309 305 if ( 'plugin' === $this->service->get_managed_by() ) {
310 306 $this->service->managed_by_plugin_action();
@@ -315,10 +311,14 @@
315 311 }
316 312
317 313 /**
318 314 * Export the data of this module, we will use this for export to HUB, create a preset etc.
315 + *
316 + * @return array
319 317 */
320 - public function to_array() {}
318 + public function to_array(): array {
319 + return array();
320 + }
321 321
322 322 /**
323 323 * Import the data from the HUB, or from the preset.
324 324 *
@@ -346,12 +346,16 @@
346 346 */
347 347 public function remove_data() {
348 348 $this->service->delete_blocklist();
349 349
350 + delete_site_option( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_NEXT_RUN_OPTION );
351 + // Don't display Antibot global notice. We can delete slugs about this notice in the future.
350 352 delete_site_option( Antibot_Global_Firewall_Component::NOTICE_SLUG );
351 - delete_site_option( Antibot_Global_Firewall_Component::DOWNLOAD_SYNC_NEXT_RUN_OPTION );
353 + delete_site_option( Antibot_Global_Firewall_Component::GLOBAL_NOTICE_TIME_OPTION );
354 + // Blocklist stats data.
352 355 delete_site_transient( Antibot_Global_Firewall_Component::BLOCKLIST_STATS_KEY . '_' . Antibot_Global_Firewall_Setting::MODE_BASIC );
353 356 delete_site_transient( Antibot_Global_Firewall_Component::BLOCKLIST_STATS_KEY . '_' . Antibot_Global_Firewall_Setting::MODE_STRICT );
357 + delete_site_transient( Antibot_Global_Firewall_Component::BLOCKLIST_STATS_FULL_KEY );
354 358 delete_site_transient( Antibot_Global_Firewall_Component::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS );
355 359 }
356 360
357 361 /**
@@ -363,19 +367,8 @@
363 367 return array();
364 368 }
365 369
366 370 /**
367 - * Disconnect site from HUB.
368 - *
369 - * @defender_route
370 - * @return Response
371 - */
372 - public function disconnect_site(): Response {
373 - $this->logout();
374 - return new Response( true, array( 'message' => __( 'Your site has been disconnected successfully!', 'defender-security' ) ) );
375 - }
376 -
377 - /**
378 371 * Handle request to switch managed by.
379 372 *
380 373 * @defender_route
381 374 * @return Response
@@ -426,9 +419,9 @@
426 419 'sanitize' => 'sanitize_text_field',
427 420 ),
428 421 'email' => array(
429 422 'type' => 'string',
430 - 'sanitize' => 'sanitize_text_field',
423 + 'sanitize' => 'sanitize_email',
431 424 ),
432 425 'service' => array(
433 426 'type' => 'string',
434 427 'sanitize' => 'sanitize_text_field',
@@ -438,18 +431,40 @@
438 431 'sanitize' => 'sanitize_text_field',
439 432 ),
440 433 )
441 434 );
435 +
436 + $reporter_email = isset( $data['email'] ) ? trim( $data['email'] ) : '';
437 + if ( '' === $reporter_email || ! is_email( $reporter_email ) ) {
438 + return new Response(
439 + false,
440 + array(
441 + 'message' => esc_html__( 'A valid email address is required.', 'defender-security' ),
442 + )
443 + );
444 + }
445 +
442 446 // Logging.
443 447 $message = sprintf(
444 448 'IP: %s, email: %s, service: %s, reason: %s',
445 449 $data['ip'],
446 - $data['email'],
450 + $reporter_email,
447 451 $data['service'],
448 452 $data['reason']
449 453 );
450 454 $this->service->log_ip_message( $message );
451 455
456 + // Track the request. Forced so it operates independently of tracking consent.
457 + $this->forced_track(
458 + 'def_request_trusted_ip',
459 + array(
460 + 'IP Address' => $data['ip'],
461 + 'User Email' => $reporter_email,
462 + 'Service' => $data['service'],
463 + 'Reason' => $data['reason'],
464 + )
465 + );
466 +
452 467 return new Response( true, array() );
453 468 }
454 469
455 470 /**
@@ -483,8 +498,17 @@
483 498
484 499 $bl_service = wd_di()->get( Blacklist_Lockout::class );
485 500 $gi_service = wd_di()->get( Global_IP::class );
486 501
502 + $ip = $data['ip'];
503 +
504 + $is_ip_allowed = $bl_service->is_ip_whitelisted( $ip )
505 + || $bl_service->is_country_whitelist( $ip )
506 + || ( $gi_service->is_global_ip_enabled() && $gi_service->is_ip_allowed( $ip ) );
507 +
508 + // Fetch the latest Custom IP blocklist from the Hub so we don't rely on stale cached data.
509 + $gi_service->fetch_global_ip_list();
510 +
487 511 return new Response(
488 512 true,
489 513 array(
490 514 'success' => true,
@@ -489,11 +513,11 @@
489 513 array(
490 514 'success' => true,
491 515 'show_notice' => false,
492 516 'message' => __( 'The IP address is searched successfully.', 'defender-security' ),
493 - 'local' => $bl_service->is_blacklist( $data['ip'] ),
494 - 'central' => $gi_service->is_global_ip_enabled() && $gi_service->is_ip_blocked( $data['ip'] ),
495 - 'antibot' => $this->service->is_ip_blocked( $data['ip'] ),
517 + 'local' => ! $is_ip_allowed && $bl_service->is_blacklist( $data['ip'] ),
518 + 'central' => ! $is_ip_allowed && $gi_service->is_global_ip_enabled() && $gi_service->is_ip_blocked( $data['ip'] ),
519 + 'antibot' => ! $is_ip_allowed && $this->service->is_ip_blocked( $ip ),
496 520 )
497 521 );
498 522 }
499 523
@@ -522,17 +546,19 @@
522 546 'message' => esc_html__( 'Invalid IP address.', 'defender-security' ),
523 547 )
524 548 );
525 549 }
526 - $collection = 'allowlist';
550 + $collection = 'allowlist';
551 + $model = wd_di()->get( \WP_Defender\Model\Setting\Blacklist_Lockout::class );
552 + $already_in_list = $model->is_ip_in_list( $ip, $collection );
553 +
527 554 // Add to Local allowlist.
528 - $model = wd_di()->get( \WP_Defender\Model\Setting\Blacklist_Lockout::class );
529 - if ( ! $model->is_ip_in_list( $ip, $collection ) ) {
555 + if ( ! $already_in_list ) {
530 556 $model->add_to_list( $ip, $collection );
531 557 }
532 558 // Add to Custom IP allowlist.
533 559 $global_ip_service = wd_di()->get( Global_IP::class );
534 - if ( $global_ip_service->can_central_ip_autosync() ) {
560 + if ( ! $already_in_list && $global_ip_service->can_central_ip_autosync() ) {
535 561 $data = array(
536 562 'allow_list' => array( $ip ),
537 563 );
538 564
@@ -546,22 +572,40 @@
546 572 );
547 573 }
548 574 }
549 575
576 + $manage_link_open = '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blocklist#tab-ip-allowlist' ) . '">';
577 + $manage_link_close = '</a>';
578 +
579 + if ( $already_in_list ) {
580 + $message = sprintf(
581 + /* translators: 1: IP address. 2: Opening anchor tag. 3: Closing anchor tag. */
582 + esc_html__(
583 + 'IP %1$s is already in your Site\'s allowlist. You can manage it in %2$sIP Lockouts%3$s.',
584 + 'defender-security'
585 + ),
586 + $ip,
587 + $manage_link_open,
588 + $manage_link_close
589 + );
590 + } else {
591 + $message = sprintf(
592 + /* translators: 1: IP address. 2: Opening anchor tag. 3: Closing anchor tag. */
593 + esc_html__(
594 + 'IP %1$s has been added to your Site\'s allowlist. You can manage it in %2$sIP Lockouts%3$s.',
595 + 'defender-security'
596 + ),
597 + $ip,
598 + $manage_link_open,
599 + $manage_link_close
600 + );
601 + }
602 +
550 603 return new Response(
551 604 true,
552 605 array(
553 - 'message' => sprintf(
554 - /* translators: 1: IP address. 2: Opening anchor tag. 3: Closing anchor tag. */
555 - esc_html__(
556 - 'IP %1$s has been added to your Site\'s allowlist. You can manage it in %2$sIP Lockouts%3$s.',
557 - 'defender-security'
558 - ),
559 - $ip,
560 - '<a href="' . network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blocklist#tab-ip-allowlist' ) . '">',
561 - '</a>'
562 - ),
563 - 'interval' => 5,
606 + 'message' => $message,
607 + 'already_in_list' => $already_in_list,
564 608 )
565 609 );
566 610 }
567 611
@@ -634,7 +678,61 @@
634 678 'data' => array(
635 679 'message' => __( 'Failed to switch. Please try again.', 'defender-security' ),
636 680 'auto_close' => true,
637 681 ),
682 + );
683 + }
684 +
685 + /**
686 + * Handle expired membership by automatically disabling the AntiBot Global Firewall module.
687 + * Logs the action when the feature is disabled due to expired membership.
688 + *
689 + * @return void
690 + */
691 + public function handle_expired_membership(): void {
692 + if ( $this->model->enabled && $this->is_expired_membership_type() ) {
693 + $this->service->managed_by_plugin_action( false );
694 + $this->log( 'AntiBot Global Firewall automatically disabled due to expired membership.', Antibot_Global_Firewall_Component::LOG_FILE_NAME );
695 + }
696 + }
697 +
698 + /**
699 + * Handle request to send feedback.
700 + *
701 + * @param Request $request The request object.
702 + *
703 + * @defender_route
704 + * @return Response
705 + */
706 + public function send_feedback( Request $request ) {
707 + $data = $request->get_data(
708 + array(
709 + 'feedback' => array( 'type' => 'string' ),
710 + )
711 + );
712 + if ( '' !== $data['feedback'] ) {
713 + // Send feedback.
714 + $this->track_feature(
715 + 'Antibot Survey',
716 + array(
717 + 'Notice Action' => 'Share feedback',
718 + 'Feedback' => $data['feedback'],
719 + )
720 + );
721 + }
722 +
723 + $message = sprintf(
724 + /* translators: 1: Open tag, 2: Close tag */
725 + __( '%1$sThanks for your feedback!%2$s It helps us make Defender even better.', 'defender-security' ),
726 + '<strong>',
727 + '</strong>'
728 + );
729 +
730 + return new Response(
731 + true,
732 + array(
733 + 'message' => $message,
734 + 'auto_close' => true,
735 + )
638 736 );
639 737 }
640 738 }