| @@ -8,12 +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; |
| 15 | +use WP_Defender\Behavior\WPMUDEV; | |
| 16 | 16 | use WP_Defender\Model\Notification\Tweak_Reminder; |
| 17 | 17 | use WP_Defender\Component\Config\Config_Hub_Helper; |
| 18 | 18 | use WP_Defender\Component\Security_Tweaks\Hide_Error; |
| 19 | 19 | use WP_Defender\Component\Security_Tweaks\WP_Version; |
| @@ -72,13 +72,33 @@ | ||
| 72 | 72 | |
| 73 | 73 | public const STATUS_ISSUES = 'issues', STATUS_RESOLVE = 'fixed', STATUS_IGNORE = 'ignore', STATUS_RESTORE = 'restore'; |
| 74 | 74 | |
| 75 | 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 | + /** | |
| 76 | 96 | * Initializes the model and service, registers routes, and sets up scheduled events if the model is active. |
| 77 | 97 | */ |
| 78 | 98 | public function __construct() { |
| 79 | 99 | $this->register_page( |
| 80 | - esc_html__( 'Recommendations', 'defender-security' ), | |
| 100 | + esc_html__( 'Hardening', 'defender-security' ), | |
| 81 | 101 | $this->slug, |
| 82 | 102 | array( $this, 'main_view' ), |
| 83 | 103 | $this->parent_slug |
| 84 | 104 | ); |
| @@ -101,19 +121,20 @@ | ||
| 101 | 121 | /** |
| 102 | 122 | * Dummy function for testing a check. |
| 103 | 123 | */ |
| 104 | 124 | public function should_output_error() { |
| 105 | - 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 ) ) { | |
| 106 | 127 | return; |
| 107 | 128 | } |
| 108 | - // It should be only trigger by admin. | |
| 129 | + // It should be triggered only by admin. | |
| 109 | 130 | if ( ! $this->check_permission() ) { |
| 110 | 131 | return; |
| 111 | 132 | } |
| 112 | 133 | |
| 113 | - $var = '$' . uniqid( '', true ); | |
| 114 | - // This should output a warning. Ignored phpcs as it's a test. | |
| 115 | - 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. | |
| 116 | 137 | exit(); |
| 117 | 138 | } |
| 118 | 139 | |
| 119 | 140 | /** |
| @@ -134,8 +155,12 @@ | ||
| 134 | 155 | 'current_server' => array( |
| 135 | 156 | 'type' => 'string', |
| 136 | 157 | 'sanitize' => 'sanitize_text_field', |
| 137 | 158 | ), |
| 159 | + 'username' => array( | |
| 160 | + 'type' => 'string', | |
| 161 | + 'sanitize' => 'sanitize_user', | |
| 162 | + ), | |
| 138 | 163 | ) |
| 139 | 164 | ); |
| 140 | 165 | |
| 141 | 166 | $slug = $data['slug'] ?? false; |
| @@ -147,8 +172,39 @@ | ||
| 147 | 172 | array( 'message' => esc_html__( 'Invalid request', 'defender-security' ) ) |
| 148 | 173 | ); |
| 149 | 174 | } |
| 150 | 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 | + | |
| 151 | 207 | if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) { |
| 152 | 208 | $current_server = $data['current_server'] ?? false; |
| 153 | 209 | if ( ! $current_server ) { |
| 154 | 210 | return new Response( |
| @@ -169,8 +225,14 @@ | ||
| 169 | 225 | $this->track_tweak( $tweak->get_label(), 'Actioned' ); |
| 170 | 226 | // Response. |
| 171 | 227 | $this->ajax_response( esc_html__( 'Security recommendation successfully resolved.', 'defender-security' ) ); |
| 172 | 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 | + } | |
| 173 | 235 | if ( is_wp_error( $ret ) ) { |
| 174 | 236 | $this->ajax_response( $ret->get_error_message(), false ); |
| 175 | 237 | } |
| 176 | 238 | |
| @@ -418,8 +480,37 @@ | ||
| 418 | 480 | } |
| 419 | 481 | } |
| 420 | 482 | |
| 421 | 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 | + /** | |
| 422 | 513 | * AJAX Response handler. |
| 423 | 514 | * |
| 424 | 515 | * @param string $message The message to be displayed in the response. |
| 425 | 516 | * @param bool $is_success Whether the response should have a success status. |
| @@ -429,22 +520,23 @@ | ||
| 429 | 520 | */ |
| 430 | 521 | private function ajax_response( $message, $is_success = true, $interval = false ): Response { |
| 431 | 522 | global $wp_version; |
| 432 | 523 | |
| 433 | - $settings = new Model_Security_Tweaks(); | |
| 434 | - $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( | |
| 435 | 527 | 'message' => $message, |
| 436 | 528 | 'summary' => array( |
| 437 | - 'issues_count' => count( $settings->issues ), | |
| 438 | - 'fixed_count' => count( $settings->fixed ), | |
| 439 | - '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 ), | |
| 440 | 532 | 'php_version' => PHP_VERSION, |
| 441 | 533 | 'wp_version' => $wp_version, |
| 442 | 534 | ), |
| 443 | - 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ), | |
| 444 | - 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ), | |
| 445 | - 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ), | |
| 446 | - '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, | |
| 447 | 539 | ); |
| 448 | 540 | if ( $interval ) { |
| 449 | 541 | $data['interval'] = $interval; |
| 450 | 542 | } |
| @@ -452,8 +544,36 @@ | ||
| 452 | 544 | return new Response( $is_success, $data ); |
| 453 | 545 | } |
| 454 | 546 | |
| 455 | 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 | + /** | |
| 456 | 576 | * Enqueues scripts and styles for this page. |
| 457 | 577 | * Only enqueues assets if the page is active. |
| 458 | 578 | */ |
| 459 | 579 | public function enqueue_assets() { |
| @@ -460,10 +580,34 @@ | ||
| 460 | 580 | if ( ! $this->is_page_active() ) { |
| 461 | 581 | return; |
| 462 | 582 | } |
| 463 | 583 | |
| 464 | - wp_localize_script( 'def-securitytweaks', 'security_tweaks', $this->data_frontend() ); | |
| 465 | - 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 | + | |
| 466 | 610 | $this->enqueue_main_assets(); |
| 467 | 611 | } |
| 468 | 612 | |
| 469 | 613 | /** |
| @@ -510,9 +654,9 @@ | ||
| 510 | 654 | } |
| 511 | 655 | |
| 512 | 656 | $tweak_arr = $this->model->get_tweak_types(); |
| 513 | 657 | |
| 514 | - $data = array( | |
| 658 | + $hardening_data = array( | |
| 515 | 659 | 'summary' => array( |
| 516 | 660 | 'fixed_count' => $tweak_arr['count_fixed'], |
| 517 | 661 | 'ignore_count' => $tweak_arr['count_ignored'], |
| 518 | 662 | 'issues_count' => $tweak_arr['count_issues'], |
| @@ -518,21 +662,20 @@ | ||
| 518 | 662 | 'issues_count' => $tweak_arr['count_issues'], |
| 519 | 663 | 'php_version' => PHP_VERSION, |
| 520 | 664 | 'wp_version' => $wp_version, |
| 521 | 665 | ), |
| 522 | - // Todo: improve the logic so that Tweak initialization happens in one go and return 3 different types. | |
| 523 | - 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ), | |
| 524 | - 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ), | |
| 525 | - '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 ), | |
| 526 | 669 | 'not_allowed_bulk' => $not_allowed_bulk, |
| 527 | 670 | 'is_autogenerate_keys' => $this->security_key->get_is_autogenerate_keys(), |
| 528 | 671 | 'reminder_frequencies' => $this->security_key->reminder_frequencies(), |
| 529 | 672 | 'enabled_user_enums' => $this->prevent_enum_users->get_enabled_user_enums(), |
| 530 | - 'hub_connector' => wd_di()->get( Hub_Connector::class )->data_frontend(), | |
| 531 | - 'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(), | |
| 532 | 673 | ); |
| 533 | 674 | |
| 534 | - return array_merge( $data, $this->dump_routes_and_nonces() ); | |
| 675 | + return array( | |
| 676 | + 'hardening' => array_merge( $hardening_data, $this->dump_routes_and_nonces() ), | |
| 677 | + ); | |
| 535 | 678 | } |
| 536 | 679 | |
| 537 | 680 | /** |
| 538 | 681 | * Renders the main view for this page. |
| @@ -565,10 +708,14 @@ | ||
| 565 | 708 | ); |
| 566 | 709 | $slugs = $data['slugs'] ?? array(); |
| 567 | 710 | $intention = $data['intention'] ?? false; |
| 568 | 711 | // Get processed and unprocessed tweaks. |
| 569 | - [ $processed, $unprocessed ] = $this->security_tweaks_auto_action( $slugs, $intention ); | |
| 712 | + [ $processed, $unprocessed, $error_message ] = $this->security_tweaks_auto_action( $slugs, $intention ); | |
| 570 | 713 | |
| 714 | + if ( null !== $error_message ) { | |
| 715 | + return $this->ajax_response( $error_message, false ); | |
| 716 | + } | |
| 717 | + | |
| 571 | 718 | $message = sprintf( |
| 572 | 719 | /* translators: 1: Either ignored or resolved, 2: Count security recommendations */ |
| 573 | 720 | esc_html__( 'You have bulk %1$s %2$s security recommendations.', 'defender-security' ), |
| 574 | 721 | 'ignore' === $intention ? 'ignored' : 'resolved', |
| @@ -600,10 +747,11 @@ | ||
| 600 | 747 | * |
| 601 | 748 | * @return array |
| 602 | 749 | */ |
| 603 | 750 | public function security_tweaks_auto_action( $slugs, $intention ): array { |
| 604 | - $processed = 0; | |
| 605 | - $unprocessed = 0; | |
| 751 | + $processed = 0; | |
| 752 | + $unprocessed = 0; | |
| 753 | + $error_message = null; | |
| 606 | 754 | |
| 607 | 755 | foreach ( $slugs as $slug ) { |
| 608 | 756 | $tweak = $this->get_tweak( $slug ); |
| 609 | 757 | if ( 'ignore' === $intention ) { |
| @@ -627,29 +775,34 @@ | ||
| 627 | 775 | } else { |
| 628 | 776 | $ret = $tweak->process(); |
| 629 | 777 | } |
| 630 | 778 | if ( is_wp_error( $ret ) ) { |
| 631 | - $this->ajax_response( | |
| 632 | - sprintf( | |
| 633 | - /* translators: 1: Security tweak title, 2: Error message */ | |
| 634 | - esc_html__( | |
| 635 | - 'There is an error while processing recommendation %1$s, error message: %2$s', | |
| 636 | - 'defender-security' | |
| 637 | - ), | |
| 638 | - $tweak->get_label(), | |
| 639 | - $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' | |
| 640 | 784 | ), |
| 641 | - false | |
| 785 | + $tweak->get_label(), | |
| 786 | + $ret->get_error_message() | |
| 642 | 787 | ); |
| 788 | + ++$unprocessed; | |
| 789 | + // Don't mark a failed tweak as resolved; stop on first error (same as original behavior). | |
| 790 | + break; | |
| 643 | 791 | } |
| 644 | 792 | $this->model->mark( self::STATUS_RESOLVE, $slug ); |
| 645 | 793 | // Track. |
| 646 | 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' ); | |
| 647 | 800 | } |
| 648 | 801 | ++$processed; |
| 649 | 802 | } |
| 650 | 803 | |
| 651 | - return array( $processed, $unprocessed ); | |
| 804 | + return array( $processed, $unprocessed, $error_message ); | |
| 652 | 805 | } |
| 653 | 806 | |
| 654 | 807 | /** |
| 655 | 808 | * Refresh the tweak status and save their state. |
| @@ -733,13 +886,19 @@ | ||
| 733 | 886 | } |
| 734 | 887 | Array_Cache::set( 'tweaks', $tweaks, 'tweaks' ); |
| 735 | 888 | } |
| 736 | 889 | $tmp = array(); |
| 737 | - if ( empty( $type ) ) { | |
| 890 | + if ( ! is_string( $type ) || '' === trim( $type ) ) { | |
| 738 | 891 | $tmp = $tweaks; |
| 739 | 892 | } else { |
| 740 | 893 | $settings = new Model_Security_Tweaks(); |
| 741 | - $compare = $settings->$type; | |
| 894 | + | |
| 895 | + try { | |
| 896 | + $compare = $settings->get_collection( $type ); | |
| 897 | + } catch ( \InvalidArgumentException $e ) { | |
| 898 | + $compare = array(); | |
| 899 | + } | |
| 900 | + | |
| 742 | 901 | foreach ( $compare as $slug ) { |
| 743 | 902 | if ( isset( $tweaks[ $slug ] ) ) { |
| 744 | 903 | $tmp[ $slug ] = $tweaks[ $slug ]; |
| 745 | 904 | } |
| @@ -833,9 +992,9 @@ | ||
| 833 | 992 | public function automate( $data, $request_reason ) { |
| 834 | 993 | $this->refresh_tweaks_status(); |
| 835 | 994 | $need_reauth = false; |
| 836 | 995 | // Resolve tweaks. |
| 837 | - if ( ! empty( $data['fixed'] ) ) { | |
| 996 | + if ( isset( $data['fixed'] ) && is_array( $data['fixed'] ) && array() !== $data['fixed'] ) { | |
| 838 | 997 | // There are some tweak that need manual apply, as files based, or change admin. |
| 839 | 998 | $manual_done = array( |
| 840 | 999 | 'replace-admin-username', |
| 841 | 1000 | 'prevent-php-executed', |
| @@ -847,9 +1006,9 @@ | ||
| 847 | 1006 | $manual_done[] = 'security-key'; |
| 848 | 1007 | } |
| 849 | 1008 | |
| 850 | 1009 | $diff_keys = array_diff( $data['fixed'], $this->model->fixed, $manual_done ); |
| 851 | - if ( ! empty( $diff_keys ) ) { | |
| 1010 | + if ( array() !== $diff_keys ) { | |
| 852 | 1011 | foreach ( $diff_keys as $slug ) { |
| 853 | 1012 | $tweak = $this->get_tweak( $slug ); |
| 854 | 1013 | if ( $tweak->has_method( 'bulk_process' ) ) { |
| 855 | 1014 | $ret = $tweak->bulk_process(); |
| @@ -880,12 +1039,12 @@ | ||
| 880 | 1039 | } |
| 881 | 1040 | } |
| 882 | 1041 | } |
| 883 | 1042 | // Revert tweaks. |
| 884 | - if ( ! empty( $data['issues'] ) ) { | |
| 1043 | + if ( isset( $data['issues'] ) && is_array( $data['issues'] ) && array() !== $data['issues'] ) { | |
| 885 | 1044 | $diff_keys = array_diff( $data['issues'], $this->model->issues ); |
| 886 | 1045 | |
| 887 | - if ( ! empty( $diff_keys ) ) { | |
| 1046 | + if ( array() !== $diff_keys ) { | |
| 888 | 1047 | // Issues. |
| 889 | 1048 | foreach ( $diff_keys as $slug ) { |
| 890 | 1049 | $tweak = $this->get_tweak( $slug ); |
| 891 | 1050 | $ret = $tweak->revert(); |
| @@ -908,11 +1067,11 @@ | ||
| 908 | 1067 | } |
| 909 | 1068 | } |
| 910 | 1069 | } |
| 911 | 1070 | // Ignore tweaks. |
| 912 | - if ( ! empty( $data['ignore'] ) ) { | |
| 1071 | + if ( isset( $data['ignore'] ) && is_array( $data['ignore'] ) && array() !== $data['ignore'] ) { | |
| 913 | 1072 | $diff_keys = array_diff( $data['ignore'], $this->model->ignore ); |
| 914 | - if ( ! empty( $diff_keys ) ) { | |
| 1073 | + if ( array() !== $diff_keys ) { | |
| 915 | 1074 | foreach ( $diff_keys as $slug ) { |
| 916 | 1075 | $this->model->mark( self::STATUS_IGNORE, $slug ); |
| 917 | 1076 | } |
| 918 | 1077 | } |
| @@ -936,9 +1095,9 @@ | ||
| 936 | 1095 | } |
| 937 | 1096 | |
| 938 | 1097 | $this->prevent_enum_users->set_enabled_user_enums( $enabled_user_enums ); |
| 939 | 1098 | |
| 940 | - 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'] ) { | |
| 941 | 1100 | $this->security_key->update_all_option( $data['security_key'] ); |
| 942 | 1101 | } |
| 943 | 1102 | |
| 944 | 1103 | $model = new Model_Security_Tweaks(); |
| @@ -959,9 +1118,9 @@ | ||
| 959 | 1118 | $settings = new Model_Security_Tweaks(); |
| 960 | 1119 | $strings = array(); |
| 961 | 1120 | $count_all = count( $settings->fixed ) + count( $settings->issues ) + count( $settings->ignore ); |
| 962 | 1121 | |
| 963 | - if ( empty( $settings->issues ) ) { | |
| 1122 | + if ( ! is_array( $settings->issues ) || array() === $settings->issues ) { | |
| 964 | 1123 | $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' ); |
| 965 | 1124 | } else { |
| 966 | 1125 | $strings[] = sprintf( |
| 967 | 1126 | /* translators: 1: Total security tweaks activated, 2: Total security tweaks */ |
| @@ -970,13 +1129,8 @@ | ||
| 970 | 1129 | $count_all |
| 971 | 1130 | ); |
| 972 | 1131 | } |
| 973 | 1132 | |
| 974 | - $tweak_notification = new Tweak_Reminder(); | |
| 975 | - if ( 'enabled' === $tweak_notification->status ) { | |
| 976 | - $strings[] = esc_html__( 'Email notifications active', 'defender-security' ); | |
| 977 | - } | |
| 978 | - | |
| 979 | 1133 | return $strings; |
| 980 | 1134 | } |
| 981 | 1135 | |
| 982 | 1136 | /** |
| @@ -982,15 +1136,18 @@ | ||
| 982 | 1136 | /** |
| 983 | 1137 | * Config strings. |
| 984 | 1138 | * |
| 985 | 1139 | * @param array $config Settings. |
| 986 | - * @param bool $is_pro True if it is a pro version. | |
| 987 | 1140 | * |
| 988 | 1141 | * @return array Strings to be displayed in the settings page. |
| 989 | 1142 | */ |
| 990 | - public function config_strings( $config, $is_pro ): array { | |
| 1143 | + public function config_strings( $config ): array { | |
| 991 | 1144 | $strings = array(); |
| 992 | - 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 | + ) { | |
| 993 | 1150 | $strings[] = esc_html__( 'All available recommendations activated', 'defender-security' ); |
| 994 | 1151 | } else { |
| 995 | 1152 | $strings[] = sprintf( |
| 996 | 1153 | /* translators: 1: Total security tweaks activated, 2: Total security tweaks */ |
| @@ -1060,9 +1217,10 @@ | ||
| 1060 | 1217 | * @return Response |
| 1061 | 1218 | * @defender_route |
| 1062 | 1219 | */ |
| 1063 | 1220 | public function update_enabled_user_enums( Request $request ): Response { |
| 1064 | - $data = (array) $request->get_data(); | |
| 1221 | + $data = $request->get_data(); | |
| 1222 | + $data = ! is_array( $data ) ? (array) $data : $data; | |
| 1065 | 1223 | $enabled_user_enums = $data['enabled_user_enums']; |
| 1066 | 1224 | $is_success = false; |
| 1067 | 1225 | $message = esc_html__( 'An error occurred, try again.', 'defender-security' ); |
| 1068 | 1226 | |