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-security-tweaks.php +224 -118 5.3.1 → 6.3.0 View file →
@@ -8,13 +8,12 @@
8 8 namespace WP_Defender\Controller;
9 9
10 10 use Countable;
11 11 use WP_Defender\Event;
12 -use WP_Defender\Admin;
13 12 use Calotes\Component\Request;
14 13 use Calotes\Component\Response;
15 14 use Calotes\Helper\Array_Cache;
16 -use WP_Defender\Component\Rate;
15 +use WP_Defender\Behavior\WPMUDEV;
17 16 use WP_Defender\Model\Notification\Tweak_Reminder;
18 17 use WP_Defender\Component\Config\Config_Hub_Helper;
19 18 use WP_Defender\Component\Security_Tweaks\Hide_Error;
20 19 use WP_Defender\Component\Security_Tweaks\WP_Version;
@@ -73,13 +72,33 @@
73 72
74 73 public const STATUS_ISSUES = 'issues', STATUS_RESOLVE = 'fixed', STATUS_IGNORE = 'ignore', STATUS_RESTORE = 'restore';
75 74
76 75 /**
76 + * Maps tweak slugs to frontend category identifiers.
77 + *
78 + * @var array<string,string>
79 + */
80 + private const TWEAK_CATEGORIES = array(
81 + 'disable-xml-rpc' => 'network',
82 + 'wp-version' => 'updates',
83 + 'hide-error' => 'error',
84 + 'php-version' => 'updates',
85 + 'replace-admin-username' => 'auth',
86 + 'security-key' => 'updates',
87 + 'login-duration' => 'auth',
88 + 'disable-trackback' => 'network',
89 + 'prevent-enum-users' => 'error',
90 + 'disable-file-editor' => 'access',
91 + 'protect-information' => 'error',
92 + 'prevent-php-executed' => 'access',
93 + );
94 +
95 + /**
77 96 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
78 97 */
79 98 public function __construct() {
80 99 $this->register_page(
81 - esc_html__( 'Recommendations', 'defender-security' ),
100 + esc_html__( 'Hardening', 'defender-security' ),
82 101 $this->slug,
83 102 array( $this, 'main_view' ),
84 103 $this->parent_slug
85 104 );
@@ -102,19 +121,20 @@
102 121 /**
103 122 * Dummy function for testing a check.
104 123 */
105 124 public function should_output_error() {
106 - if ( empty( defender_get_data_from_request( 'defender_test_error_reporting', 'g' ) ) ) {
125 + $test_err_reporting = defender_get_data_from_request( 'defender_test_error_reporting', 'g' );
126 + if ( ! is_string( $test_err_reporting ) || '' === trim( $test_err_reporting ) ) {
107 127 return;
108 128 }
109 - // It should be only trigger by admin.
129 + // It should be triggered only by admin.
110 130 if ( ! $this->check_permission() ) {
111 131 return;
112 132 }
113 133
114 - $var = '$' . uniqid( '', true );
115 - // This should output a warning. Ignored phpcs as it's a test.
116 - echo ${$var}; // phpcs:ignore
134 + // This should output a warning. Ignored by PHPCS as it's a test.
135 + // @phpstan-ignore-next-line.
136 + echo $undefined_variable; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentionally outputs an undefined variable to test warning handling.
117 137 exit();
118 138 }
119 139
120 140 /**
@@ -135,8 +155,12 @@
135 155 'current_server' => array(
136 156 'type' => 'string',
137 157 'sanitize' => 'sanitize_text_field',
138 158 ),
159 + 'username' => array(
160 + 'type' => 'string',
161 + 'sanitize' => 'sanitize_user',
162 + ),
139 163 )
140 164 );
141 165
142 166 $slug = $data['slug'] ?? false;
@@ -148,8 +172,39 @@
148 172 array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) )
149 173 );
150 174 }
151 175
176 + if ( 'prevent-enum-users' === $slug ) {
177 + $pe_data = $request->get_data();
178 + $enabled_enums = $pe_data['enabled_user_enums'] ?? null;
179 + $all_enums = array( 'enum-rest', 'enum-oembed', 'enum-sitemap' );
180 + $this->prevent_enum_users->set_enabled_user_enums(
181 + is_array( $enabled_enums ) ? $enabled_enums : $all_enums
182 + );
183 + }
184 +
185 + if ( 'security-key' === $slug ) {
186 + $sk_data = $request->get_data();
187 + $remind_date = sanitize_text_field( $sk_data['remind_date'] ?? '' );
188 + if ( $remind_date ) {
189 + $is_autogen = filter_var( $sk_data['is_autogenerate_keys'] ?? false, FILTER_VALIDATE_BOOLEAN );
190 + $values = get_site_option( 'defender_security_tweaks_' . $this->security_key->slug, array() );
191 + $values['reminder_duration'] = $remind_date;
192 + $values['reminder_date'] = strtotime( '+' . $remind_date, time() );
193 + $values['is_autogenerate_keys'] = $is_autogen;
194 + update_site_option( 'defender_security_tweaks_' . $this->security_key->slug, $values );
195 + if ( true === $is_autogen ) {
196 + $this->security_key->cron_schedule();
197 + } else {
198 + $this->security_key->cron_unschedule();
199 + }
200 + }
201 + }
202 +
203 + if ( 'replace-admin-username' === $slug && isset( $data['username'] ) && '' !== trim( $data['username'] ) ) {
204 + $_POST['username'] = $data['username'];
205 + }
206 +
152 207 if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) {
153 208 $current_server = $data['current_server'] ?? false;
154 209 if ( ! $current_server ) {
155 210 return new Response(
@@ -170,8 +225,14 @@
170 225 $this->track_tweak( $tweak->get_label(), 'Actioned' );
171 226 // Response.
172 227 $this->ajax_response( esc_html__( 'Security recommendation successfully resolved.', 'defender-security' ) );
173 228 }
229 + if ( $ret instanceof Response && true === $ret->status ) {
230 + Config_Hub_Helper::set_clear_active_flag();
231 + $this->model->mark( self::STATUS_RESOLVE, $slug );
232 + $this->track_tweak( $tweak->get_label(), 'Actioned' );
233 + return $ret;
234 + }
174 235 if ( is_wp_error( $ret ) ) {
175 236 $this->ajax_response( $ret->get_error_message(), false );
176 237 }
177 238
@@ -269,11 +330,12 @@
269 330 }
270 331 $this->model->mark( self::STATUS_IGNORE, $slug );
271 332 // Track.
272 333 $this->track_tweak( $tweak->get_label(), 'Ignored' );
334 + if ( 'security-key' === $slug ) {
335 + $this->security_key->cron_unschedule();
336 + }
273 337
274 - $this->security_key->cron_unschedule();
275 -
276 338 $this->ajax_response( esc_html__( 'Security recommendation successfully ignored.', 'defender-security' ) );
277 339 }
278 340
279 341 /**
@@ -305,10 +367,8 @@
305 367 // Track.
306 368 $this->track_tweak( $tweak->get_label(), 'Restored' );
307 369
308 370 if ( $this->security_key->get_is_autogenerate_keys() ) {
309 - // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job.
310 - $this->security_key->cron_unschedule();
311 371 $this->security_key->cron_schedule();
312 372 }
313 373
314 374 $this->ajax_response( esc_html__( 'Security recommendation successfully restored.', 'defender-security' ) );
@@ -402,11 +462,11 @@
402 462
403 463 if ( update_site_option( 'defender_security_tweaks_' . $this->security_key->slug, $values ) ) {
404 464
405 465 if ( true === $is_autogen_flag ) {
406 - // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job.
466 + $this->security_key->cron_schedule();
467 + } else {
407 468 $this->security_key->cron_unschedule();
408 - $this->security_key->cron_schedule();
409 469 }
410 470
411 471 return new Response(
412 472 true,
@@ -420,8 +480,37 @@
420 480 }
421 481 }
422 482
423 483 /**
484 + * Returns tweaks for a given status formatted for the React frontend.
485 + *
486 + * Adds `category` and remaps the backend status string to the value the
487 + * frontend HARDENING_STATUS constants expect ('issue', 'resolved', 'ignored').
488 + *
489 + * @param string $status One of STATUS_ISSUES, STATUS_RESOLVE, STATUS_IGNORE.
490 + *
491 + * @return array
492 + */
493 + private function format_tweaks_for_frontend( string $status ): array {
494 + $status_map = array(
495 + self::STATUS_ISSUES => 'issue',
496 + self::STATUS_RESOLVE => 'resolved',
497 + self::STATUS_IGNORE => 'ignored',
498 + );
499 +
500 + $tweaks = $this->init_tweaks( $status, 'array' );
501 + $frontend_status = $status_map[ $status ] ?? $status;
502 +
503 + foreach ( $tweaks as &$tweak ) {
504 + $tweak['status'] = $frontend_status;
505 + $tweak['category'] = self::TWEAK_CATEGORIES[ $tweak['slug'] ] ?? 'access';
506 + }
507 + unset( $tweak );
508 +
509 + return $tweaks;
510 + }
511 +
512 + /**
424 513 * AJAX Response handler.
425 514 *
426 515 * @param string $message The message to be displayed in the response.
427 516 * @param bool $is_success Whether the response should have a success status.
@@ -431,22 +520,23 @@
431 520 */
432 521 private function ajax_response( $message, $is_success = true, $interval = false ): Response {
433 522 global $wp_version;
434 523
435 - $settings = new Model_Security_Tweaks();
436 - $data = array(
524 + // Creating a new Model_Security_Tweaks() here risks reading stale data from the
525 + // DB when the WP options cache hasn't flushed the write yet.
526 + $data = array(
437 527 'message' => $message,
438 528 'summary' => array(
439 - 'issues_count' => count( $settings->issues ),
440 - 'fixed_count' => count( $settings->fixed ),
441 - 'ignore_count' => count( $settings->ignore ),
529 + 'issues_count' => count( $this->model->issues ),
530 + 'fixed_count' => count( $this->model->fixed ),
531 + 'ignore_count' => count( $this->model->ignore ),
442 532 'php_version' => PHP_VERSION,
443 533 'wp_version' => $wp_version,
444 534 ),
445 - 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ),
446 - 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ),
447 - 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ),
448 - 'issues_slugs' => $settings->issues,
535 + 'issues' => $this->build_tweaks_response( $this->model->issues, 'issue' ),
536 + 'fixed' => $this->build_tweaks_response( $this->model->fixed, 'resolved' ),
537 + 'ignored' => $this->build_tweaks_response( $this->model->ignore, 'ignored' ),
538 + 'issues_slugs' => $this->model->issues,
449 539 );
450 540 if ( $interval ) {
451 541 $data['interval'] = $interval;
452 542 }
@@ -454,8 +544,36 @@
454 544 return new Response( $is_success, $data );
455 545 }
456 546
457 547 /**
548 + * Builds the frontend-shaped tweak array from a list of slugs.
549 + *
550 + * Uses the in-memory tweak object cache rather than re-querying the model,
551 + * so the result is always consistent with the state after mark() was called.
552 + *
553 + * @param array $slugs Slugs to include.
554 + * @param string $frontend_status Frontend status string ('issue', 'resolved', 'ignored').
555 + *
556 + * @return array
557 + */
558 + private function build_tweaks_response( array $slugs, string $frontend_status ): array {
559 + $tweaks = Array_Cache::get( 'tweaks', 'tweaks' );
560 + $result = array();
561 +
562 + foreach ( $slugs as $slug ) {
563 + if ( ! isset( $tweaks[ $slug ] ) ) {
564 + continue;
565 + }
566 + $arr = $tweaks[ $slug ]->to_array();
567 + $arr['status'] = $frontend_status;
568 + $arr['category'] = self::TWEAK_CATEGORIES[ $slug ] ?? 'access';
569 + $result[ $slug ] = $arr;
570 + }
571 +
572 + return $result;
573 + }
574 +
575 + /**
458 576 * Enqueues scripts and styles for this page.
459 577 * Only enqueues assets if the page is active.
460 578 */
461 579 public function enqueue_assets() {
@@ -462,10 +580,34 @@
462 580 if ( ! $this->is_page_active() ) {
463 581 return;
464 582 }
465 583
466 - wp_localize_script( 'def-securitytweaks', 'security_tweaks', $this->data_frontend() );
467 - wp_enqueue_script( 'def-securitytweaks' );
584 + $handle = 'defender-ui-hardening';
585 + wp_enqueue_script(
586 + $handle,
587 + WP_DEFENDER_BASE_URL . 'assets/js/hardening-ui.js',
588 + array( 'def-vue', 'def-manifest', 'def-core-ui', 'defender', 'wp-i18n' ),
589 + DEFENDER_VERSION,
590 + true
591 + );
592 + wp_set_script_translations( $handle, 'wpdef' );
593 +
594 + wp_localize_script(
595 + $handle,
596 + 'defenderUIData',
597 + array_merge(
598 + $this->get_shared_data(),
599 + $this->data_frontend()
600 + )
601 + );
602 +
603 + wp_enqueue_style(
604 + $handle,
605 + WP_DEFENDER_BASE_URL . 'assets/css/core-ui.css',
606 + array(),
607 + DEFENDER_VERSION
608 + );
609 +
468 610 $this->enqueue_main_assets();
469 611 }
470 612
471 613 /**
@@ -510,34 +652,11 @@
510 652 $not_allowed_bulk[] = 'protect-information';
511 653 $not_allowed_bulk[] = 'prevent-php-executed';
512 654 }
513 655
514 - $tweak_arr = $this->model->get_tweak_types();
515 - $total_tweaks = $tweak_arr['count_fixed'] + $tweak_arr['count_ignored'] + $tweak_arr['count_issues'];
656 + $tweak_arr = $this->model->get_tweak_types();
516 657
517 - // Prepare additional data.
518 - if ( defender_is_wp_org_version() ) {
519 - $misc = array(
520 - 'rating_is_displayed' => ! Rate::was_rate_request() && $tweak_arr['count_fixed'] === $total_tweaks,
521 - 'rating_text' => sprintf(
522 - /* translators: %d - Total number. */
523 - esc_html__(
524 - '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.',
525 - 'defender-security'
526 - ),
527 - $total_tweaks
528 - ),
529 - 'rating_type' => 'tweak',
530 - );
531 - } else {
532 - $misc = array(
533 - 'rating_is_displayed' => false,
534 - 'rating_text' => '',
535 - 'rating_type' => '',
536 - );
537 - }
538 -
539 - $data = array(
658 + $hardening_data = array(
540 659 'summary' => array(
541 660 'fixed_count' => $tweak_arr['count_fixed'],
542 661 'ignore_count' => $tweak_arr['count_ignored'],
543 662 'issues_count' => $tweak_arr['count_issues'],
@@ -543,20 +662,20 @@
543 662 'issues_count' => $tweak_arr['count_issues'],
544 663 'php_version' => PHP_VERSION,
545 664 'wp_version' => $wp_version,
546 665 ),
547 - // Todo: improve the logic so that Tweak initialization happens in one go and return 3 different types.
548 - 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ),
549 - 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ),
550 - 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ),
666 + 'issues' => $this->format_tweaks_for_frontend( self::STATUS_ISSUES ),
667 + 'fixed' => $this->format_tweaks_for_frontend( self::STATUS_RESOLVE ),
668 + 'ignored' => $this->format_tweaks_for_frontend( self::STATUS_IGNORE ),
551 669 'not_allowed_bulk' => $not_allowed_bulk,
552 670 'is_autogenerate_keys' => $this->security_key->get_is_autogenerate_keys(),
553 671 'reminder_frequencies' => $this->security_key->reminder_frequencies(),
554 672 'enabled_user_enums' => $this->prevent_enum_users->get_enabled_user_enums(),
555 - 'misc' => $misc,
556 673 );
557 674
558 - return array_merge( $data, $this->dump_routes_and_nonces() );
675 + return array(
676 + 'hardening' => array_merge( $hardening_data, $this->dump_routes_and_nonces() ),
677 + );
559 678 }
560 679
561 680 /**
562 681 * Renders the main view for this page.
@@ -589,10 +708,14 @@
589 708 );
590 709 $slugs = $data['slugs'] ?? array();
591 710 $intention = $data['intention'] ?? false;
592 711 // Get processed and unprocessed tweaks.
593 - [ $processed, $unprocessed ] = $this->security_tweaks_auto_action( $slugs, $intention );
712 + [ $processed, $unprocessed, $error_message ] = $this->security_tweaks_auto_action( $slugs, $intention );
594 713
714 + if ( null !== $error_message ) {
715 + return $this->ajax_response( $error_message, false );
716 + }
717 +
595 718 $message = sprintf(
596 719 /* translators: 1: Either ignored or resolved, 2: Count security recommendations */
597 720 esc_html__( 'You have bulk %1$s %2$s security recommendations.', 'defender-security' ),
598 721 'ignore' === $intention ? 'ignored' : 'resolved',
@@ -624,10 +747,11 @@
624 747 *
625 748 * @return array
626 749 */
627 750 public function security_tweaks_auto_action( $slugs, $intention ): array {
628 - $processed = 0;
629 - $unprocessed = 0;
751 + $processed = 0;
752 + $unprocessed = 0;
753 + $error_message = null;
630 754
631 755 foreach ( $slugs as $slug ) {
632 756 $tweak = $this->get_tweak( $slug );
633 757 if ( 'ignore' === $intention ) {
@@ -651,29 +775,34 @@
651 775 } else {
652 776 $ret = $tweak->process();
653 777 }
654 778 if ( is_wp_error( $ret ) ) {
655 - $this->ajax_response(
656 - sprintf(
657 - /* translators: 1: Security tweak title, 2: Error message */
658 - esc_html__(
659 - 'There is an error while processing recommendation %1$s, error message: %2$s',
660 - 'defender-security'
661 - ),
662 - $tweak->get_label(),
663 - $ret->get_error_message()
779 + $error_message = sprintf(
780 + /* translators: 1: Security tweak title, 2: Error message */
781 + esc_html__(
782 + 'There is an error while processing recommendation %1$s, error message: %2$s',
783 + 'defender-security'
664 784 ),
665 - false
785 + $tweak->get_label(),
786 + $ret->get_error_message()
666 787 );
788 + ++$unprocessed;
789 + // Don't mark a failed tweak as resolved; stop on first error (same as original behavior).
790 + break;
667 791 }
668 792 $this->model->mark( self::STATUS_RESOLVE, $slug );
669 793 // Track.
670 794 $this->track_tweak( $tweak->get_label(), 'Actioned' );
795 + } elseif ( 'restore' === $intention ) {
796 + // New bulk action since V6.
797 + $this->model->mark( self::STATUS_ISSUES, $slug );
798 + // Track.
799 + $this->track_tweak( $tweak->get_label(), 'Restored' );
671 800 }
672 801 ++$processed;
673 802 }
674 803
675 - return array( $processed, $unprocessed );
804 + return array( $processed, $unprocessed, $error_message );
676 805 }
677 806
678 807 /**
679 808 * Refresh the tweak status and save their state.
@@ -757,13 +886,19 @@
757 886 }
758 887 Array_Cache::set( 'tweaks', $tweaks, 'tweaks' );
759 888 }
760 889 $tmp = array();
761 - if ( empty( $type ) ) {
890 + if ( ! is_string( $type ) || '' === trim( $type ) ) {
762 891 $tmp = $tweaks;
763 892 } else {
764 893 $settings = new Model_Security_Tweaks();
765 - $compare = $settings->$type;
894 +
895 + try {
896 + $compare = $settings->get_collection( $type );
897 + } catch ( \InvalidArgumentException $e ) {
898 + $compare = array();
899 + }
900 +
766 901 foreach ( $compare as $slug ) {
767 902 if ( isset( $tweaks[ $slug ] ) ) {
768 903 $tmp[ $slug ] = $tweaks[ $slug ];
769 904 }
@@ -832,9 +967,9 @@
832 967 ( new Model_Security_Tweaks() )->delete();
833 968
834 969 delete_site_transient( Server::CACHE_CURRENT_SERVER );
835 970 delete_site_transient( \WP_Defender\Component\Security_Tweaks\Servers\Apache::CACHE_APACHE_VERSION );
836 - wp_clear_scheduled_hook( 'wpdef_sec_key_gen' );
971 + $this->security_key->cron_unschedule();
837 972 }
838 973
839 974 /**
840 975 * Delete all the data & the cache.
@@ -857,9 +992,9 @@
857 992 public function automate( $data, $request_reason ) {
858 993 $this->refresh_tweaks_status();
859 994 $need_reauth = false;
860 995 // Resolve tweaks.
861 - if ( ! empty( $data['fixed'] ) ) {
996 + if ( isset( $data['fixed'] ) && is_array( $data['fixed'] ) && array() !== $data['fixed'] ) {
862 997 // There are some tweak that need manual apply, as files based, or change admin.
863 998 $manual_done = array(
864 999 'replace-admin-username',
865 1000 'prevent-php-executed',
@@ -871,9 +1006,9 @@
871 1006 $manual_done[] = 'security-key';
872 1007 }
873 1008
874 1009 $diff_keys = array_diff( $data['fixed'], $this->model->fixed, $manual_done );
875 - if ( ! empty( $diff_keys ) ) {
1010 + if ( array() !== $diff_keys ) {
876 1011 foreach ( $diff_keys as $slug ) {
877 1012 $tweak = $this->get_tweak( $slug );
878 1013 if ( $tweak->has_method( 'bulk_process' ) ) {
879 1014 $ret = $tweak->bulk_process();
@@ -904,12 +1039,12 @@
904 1039 }
905 1040 }
906 1041 }
907 1042 // Revert tweaks.
908 - if ( ! empty( $data['issues'] ) ) {
1043 + if ( isset( $data['issues'] ) && is_array( $data['issues'] ) && array() !== $data['issues'] ) {
909 1044 $diff_keys = array_diff( $data['issues'], $this->model->issues );
910 1045
911 - if ( ! empty( $diff_keys ) ) {
1046 + if ( array() !== $diff_keys ) {
912 1047 // Issues.
913 1048 foreach ( $diff_keys as $slug ) {
914 1049 $tweak = $this->get_tweak( $slug );
915 1050 $ret = $tweak->revert();
@@ -932,11 +1067,11 @@
932 1067 }
933 1068 }
934 1069 }
935 1070 // Ignore tweaks.
936 - if ( ! empty( $data['ignore'] ) ) {
1071 + if ( isset( $data['ignore'] ) && is_array( $data['ignore'] ) && array() !== $data['ignore'] ) {
937 1072 $diff_keys = array_diff( $data['ignore'], $this->model->ignore );
938 - if ( ! empty( $diff_keys ) ) {
1073 + if ( array() !== $diff_keys ) {
939 1074 foreach ( $diff_keys as $slug ) {
940 1075 $this->model->mark( self::STATUS_IGNORE, $slug );
941 1076 }
942 1077 }
@@ -960,9 +1095,9 @@
960 1095 }
961 1096
962 1097 $this->prevent_enum_users->set_enabled_user_enums( $enabled_user_enums );
963 1098
964 - if ( ! empty( $data['security_key'] ) && is_array( $data['security_key'] ) ) {
1099 + if ( isset( $data['security_key'] ) && is_array( $data['security_key'] ) && array() !== $data['security_key'] ) {
965 1100 $this->security_key->update_all_option( $data['security_key'] );
966 1101 }
967 1102
968 1103 $model = new Model_Security_Tweaks();
@@ -983,9 +1118,9 @@
983 1118 $settings = new Model_Security_Tweaks();
984 1119 $strings = array();
985 1120 $count_all = count( $settings->fixed ) + count( $settings->issues ) + count( $settings->ignore );
986 1121
987 - if ( empty( $settings->issues ) ) {
1122 + if ( ! is_array( $settings->issues ) || array() === $settings->issues ) {
988 1123 $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' );
989 1124 } else {
990 1125 $strings[] = sprintf(
991 1126 /* translators: 1: Total security tweaks activated, 2: Total security tweaks */
@@ -994,13 +1129,8 @@
994 1129 $count_all
995 1130 );
996 1131 }
997 1132
998 - $tweak_notification = new Tweak_Reminder();
999 - if ( 'enabled' === $tweak_notification->status ) {
1000 - $strings[] = esc_html__( 'Email notifications active', 'defender-security' );
1001 - }
1002 -
1003 1133 return $strings;
1004 1134 }
1005 1135
1006 1136 /**
@@ -1006,15 +1136,18 @@
1006 1136 /**
1007 1137 * Config strings.
1008 1138 *
1009 1139 * @param array $config Settings.
1010 - * @param bool $is_pro True if it is a pro version.
1011 1140 *
1012 1141 * @return array Strings to be displayed in the settings page.
1013 1142 */
1014 - public function config_strings( $config, $is_pro ): array {
1143 + public function config_strings( $config ): array {
1015 1144 $strings = array();
1016 - if ( empty( $config['issues'] ) ) {
1145 + if (
1146 + ! isset( $config['issues'] )
1147 + || ! ( is_array( $config['issues'] ) || $config['issues'] instanceof Countable )
1148 + || 0 === count( $config['issues'] )
1149 + ) {
1017 1150 $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' );
1018 1151 } else {
1019 1152 $strings[] = sprintf(
1020 1153 /* translators: 1: Total security tweaks activated, 2: Total security tweaks */
@@ -1084,9 +1217,10 @@
1084 1217 * @return Response
1085 1218 * @defender_route
1086 1219 */
1087 1220 public function update_enabled_user_enums( Request $request ): Response {
1088 - $data = (array) $request->get_data();
1221 + $data = $request->get_data();
1222 + $data = ! is_array( $data ) ? (array) $data : $data;
1089 1223 $enabled_user_enums = $data['enabled_user_enums'];
1090 1224 $is_success = false;
1091 1225 $message = esc_html__( 'An error occurred, try again.', 'defender-security' );
1092 1226
@@ -1098,36 +1232,8 @@
1098 1232 return new Response(
1099 1233 $is_success,
1100 1234 array( 'message' => $message )
1101 1235 );
1102 - }
1103 -
1104 - /**
1105 - * Handle tweaks rating notice.
1106 - *
1107 - * @param Request $request Request.
1108 - *
1109 - * @return Response
1110 - * @defender_route
1111 - */
1112 - public function handle_notice( Request $request ): Response {
1113 - update_site_option( Rate::SLUG_FOR_BUTTON_RATE, true );
1114 -
1115 - return new Response( true, array() );
1116 - }
1117 -
1118 - /**
1119 - * Attention: Tweaks rating notice doesn't have postpone_notice route.
1120 - *
1121 - * @param Request $request Request object.
1122 - *
1123 - * @defender_route
1124 - * @return Response
1125 - */
1126 - public function refuse_notice( Request $request ): Response {
1127 - update_site_option( Rate::SLUG_FOR_BUTTON_THANKS, true );
1128 -
1129 - return new Response( true, array() );
1130 1236 }
1131 1237
1132 1238 /**
1133 1239 * Check XML-RPC status.