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-scan.php +60 -86 6.0.1 → 6.3.0 View file →
@@ -79,13 +79,13 @@
79 79 */
80 80 private $quarantine_controller;
81 81
82 82 /**
83 - * Is the Hub API key available?
83 + * Is the Malware Report option unlocked?
84 84 *
85 85 * @var bool
86 86 */
87 - private $is_apikey;
87 + private $is_unlocked;
88 88
89 89 /**
90 90 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
91 91 */
@@ -96,14 +96,13 @@
96 96 array( $this, 'main_view' ),
97 97 $this->parent_slug
98 98 );
99 99
100 - $this->model = new Scan_Settings();
100 + $this->model = wd_di()->get( Scan_Settings::class );
101 101 $this->service = wd_di()->get( Scan_Component::class );
102 102 $this->quarantine_controller = wd_di()->get( Quarantine::class );
103 - $wpmudev = wd_di()->get( WPMUDEV::class );
104 103
105 - $this->is_apikey = false !== $wpmudev->get_apikey();
104 + $this->is_unlocked = wd_di()->get( WPMUDEV::class )->is_pro();
106 105
107 106 $this->register_routes();
108 107 add_action( 'defender_enqueue_assets', array( $this, 'enqueue_assets' ) );
109 108 add_action( 'wp_ajax_defender_process_scan', array( $this, 'process' ) );
@@ -117,9 +116,9 @@
117 116 if (
118 117 is_admin() &&
119 118 'plugins.php' === $pagenow &&
120 119 apply_filters( 'wd_display_vulnerability_warnings', true ) &&
121 - $this->is_apikey
120 + wd_di()->get( WPMUDEV::class )->is_apikey_available()
122 121 ) {
123 122 $this->service->display_vulnerability_warnings();
124 123 }
125 124
@@ -218,16 +217,14 @@
218 217 * @is_public
219 218 */
220 219 public function process() {
221 220 $lock_filename = $this->service->get_lock_filename();
222 - if ( $this->service->has_lock( $lock_filename ) ) {
221 + if ( ! $this->service->try_create_lock( $lock_filename ) ) {
223 222 $this->log( 'Fallback as already a process is running', self::SCAN_LOG );
224 223
225 224 return;
226 225 }
227 226
228 - // This creates file lock, for make sure only 1 process run as a time.
229 - $this->service->create_lock( $lock_filename );
230 227 // Check if the ping is from self or not.
231 228 $ret = $this->service->process();
232 229 $this->log( 'process done, queue for next', self::SCAN_LOG );
233 230 if ( false === $ret ) {
@@ -253,10 +250,9 @@
253 250 $idle_scan = wd_di()->get( Model_Scan::class )->get_idle();
254 251
255 252 if ( is_object( $idle_scan ) ) {
256 253 $this->service->update_idle_scan_status();
257 - $response = $idle_scan->to_array();
258 - $response['scan_type'] = $scan_type;
254 + $response = $this->get_status_response_data( $idle_scan, $scan_type );
259 255
260 256 return new Response( true, $response );
261 257 }
262 258
@@ -263,10 +259,9 @@
263 259 $checksum_issue = get_site_option( Core_Integrity::ISSUE_CHECKSUMS, 'false' );
264 260 $checksum_scan = Model_Scan::get_core_check();
265 261 if ( 'false' !== $checksum_issue && is_object( $checksum_scan ) ) {
266 262 $this->service->update_idle_scan_status_by_checksum_issue( $checksum_scan );
267 - $response = $checksum_scan->to_array();
268 - $response['scan_type'] = $scan_type;
263 + $response = $this->get_status_response_data( $checksum_scan, $scan_type );
269 264
270 265 return new Response( true, $response );
271 266 }
272 267
@@ -271,10 +266,9 @@
271 266 }
272 267
273 268 $scan = Model_Scan::get_active();
274 269 if ( is_object( $scan ) ) {
275 - $response = $scan->to_array();
276 - $response['scan_type'] = $scan_type;
270 + $response = $this->get_status_response_data( $scan, $scan_type );
277 271
278 272 return new Response( true, $response );
279 273 }
280 274
@@ -279,9 +273,9 @@
279 273 }
280 274
281 275 $scan = Model_Scan::get_last();
282 276 if ( is_object( $scan ) && ! is_wp_error( $scan ) ) {
283 - $response = array_merge( $scan->to_array(), $this->get_last_scan_time_data( $scan ) );
277 + $response = array_merge( $this->get_status_response_data( $scan, $scan_type ), $this->get_last_scan_time_data( $scan ) );
284 278 $response['message'] = __( 'Malware scan completed successfully!', 'defender-security' );
285 279 $response['scan_type'] = $scan_type;
286 280 if ( 'deep' === $scan_type ) {
287 281 $security_tweaks = wd_di()->get( \WP_Defender\Controller\Security_Tweaks::class )->dashboard_widget();
@@ -302,8 +296,24 @@
302 296 );
303 297 }
304 298
305 299 /**
300 + * Build the compatible status payload and consume its queued messages.
301 + *
302 + * @param Model_Scan $scan Scan model used to build the status payload.
303 + * @param string $scan_type Current scan type.
304 + */
305 + private function get_status_response_data( Model_Scan $scan, string $scan_type ): array {
306 + $response = $scan->to_array();
307 + $response['status_text'] = $response['status_text'] ?? $scan->get_status_text();
308 + $response['percent'] = $response['percent'] ?? $scan->percent;
309 + $response['scan_type'] = $scan_type;
310 + $response['status_messages'] = $scan->drain_status_messages();
311 +
312 + return $response;
313 + }
314 +
315 + /**
306 316 * Cancel current scan.
307 317 *
308 318 * @return Response
309 319 * @defender_route
@@ -643,9 +653,9 @@
643 653 $before_import_schedule,
644 654 $data['quarantine_expire_schedule']
645 655 );
646 656 }
647 - // Todo: need to disable Malware_Notification if all scan settings are deactivated?
657 +
648 658 $this->model->save();
649 659 Config_Hub_Helper::set_clear_active_flag();
650 660
651 661 return new Response(
@@ -767,9 +777,9 @@
767 777 *
768 778 * @return array
769 779 */
770 780 private function get_last_scan_time_data( $last ): array {
771 - if ( ! is_object( $last ) || empty( $last->date_start ) ) {
781 + if ( ! is_object( $last ) || ! isset( $last->date_start ) || '' === $last->date_start ) {
772 782 return array(
773 783 'last_scan' => '',
774 784 'last_scan_time' => '',
775 785 );
@@ -778,10 +788,10 @@
778 788 $last_scan_timestamp = strtotime( $last->date_start . ' UTC' );
779 789 $time_difference = time() - $last_scan_timestamp;
780 790 $data = array(
781 791 'last_scan' => sprintf(
782 - /* translators: %s: human-readable time difference, e.g. "5 minutes" */
783 - __( '%s ago', 'defender-security' ),
792 + /* translators: %s: human-readable time difference, e.g. "5 minutes ago" */
793 + __( 'Scanned %s ago', 'defender-security' ),
784 794 human_time_diff( $last_scan_timestamp )
785 795 ),
786 796 );
787 797
@@ -808,26 +818,24 @@
808 818 $paged = 1;
809 819
810 820 if ( ! is_object( $scan ) && ! is_object( $last ) ) {
811 821 $scan_data = null;
812 - } else {
822 + } elseif ( is_object( $scan ) && is_object( $last ) ) {
813 823 // If an active scan exists AND there's a previous completed scan,
814 824 // merge the active scan's progress with the last scan's issue data.
815 825 // This ensures that during a page refresh while scanning, users still
816 826 // see the previous scan results while the new scan is in progress.
817 - if ( is_object( $scan ) && is_object( $last ) ) {
818 - $scan_data = $scan->to_array( $per_page, $paged );
819 - $last_data = $last->to_array( $per_page, $paged );
820 - // Preserve previous scan's issue data.
821 - $scan_data['issues_items'] = $last_data['issues_items'] ?? array();
822 - $scan_data['ignored_items'] = $last_data['ignored_items'] ?? array();
823 - $scan_data['count'] = $last_data['count'] ?? array();
824 - $scan_data['paging'] = $last_data['paging'] ?? array();
825 - } elseif ( is_object( $scan ) ) {
826 - $scan_data = $scan->to_array( $per_page, $paged );
827 - } else {
828 - $scan_data = $last->to_array( $per_page, $paged );
829 - }
827 + $scan_data = $scan->to_array( $per_page, $paged );
828 + $last_data = $last->to_array( $per_page, $paged );
829 + // Preserve previous scan's issue data.
830 + $scan_data['issues_items'] = $last_data['issues_items'] ?? array();
831 + $scan_data['ignored_items'] = $last_data['ignored_items'] ?? array();
832 + $scan_data['count'] = $last_data['count'] ?? array();
833 + $scan_data['paging'] = $last_data['paging'] ?? array();
834 + } elseif ( is_object( $scan ) ) {
835 + $scan_data = $scan->to_array( $per_page, $paged );
836 + } else {
837 + $scan_data = $last->to_array( $per_page, $paged );
830 838 }
831 839
832 840 $first_scan_started = get_site_option( self::FIRST_SCAN_STARTED, null );
833 841 if ( null === $first_scan_started ) {
@@ -837,10 +845,11 @@
837 845 $first_scan_started = '1' === (string) $first_scan_started;
838 846 }
839 847
840 848 $data = array(
841 - 'scan' => $scan_data,
849 + 'scan' => $scan_data,
842 850 'has_first_scan_started' => $first_scan_started,
851 + 'isEnabledScanType' => $this->service->is_any_scan_type_active(),
843 852 );
844 853
845 854 // Always expose the last completed scan time at the outer level so the
846 855 // dashboard can show it even while a new scan is actively running.
@@ -859,9 +868,9 @@
859 868 return array( 'scan' => $data );
860 869 }
861 870
862 871 /**
863 - * Render main page.
872 + * Render the main page.
864 873 *
865 874 * @return void
866 875 */
867 876 public function main_view(): void {
@@ -920,9 +929,9 @@
920 929 );
921 930
922 931 wp_enqueue_style(
923 932 $handle,
924 - WP_DEFENDER_BASE_URL . 'assets/css/showcase.css',
933 + WP_DEFENDER_BASE_URL . 'assets/css/core-ui.css',
925 934 array(),
926 935 DEFENDER_VERSION
927 936 );
928 937
@@ -968,8 +977,9 @@
968 977 public function remove_data(): void {
969 978 delete_site_option( self::FIRST_SCAN_STARTED );
970 979 delete_site_option( Model_Scan::IGNORE_INDEXER );
971 980 delete_site_option( Model_Scan::OPTION_SCAN_TYPE );
981 + Model_Scan::clear_all_status_messages();
972 982 delete_site_option( Core_Integrity::ISSUE_CHECKSUMS );
973 983 delete_site_transient( Plugin_Integrity::$org_slugs );
974 984 delete_site_transient( Plugin_Integrity::$org_responses );
975 985 }
@@ -988,35 +998,20 @@
988 998 $scan = null;
989 999 } else {
990 1000 $scan = is_object( $scan ) ? $scan->to_array( $per_page, $paged ) : $last->to_array( $per_page, $paged );
991 1001 }
992 - $settings = new Scan_Settings();
993 - $report = wd_di()->get( Malware_Report::class );
994 - $report_text = esc_html__( 'Automatic scans are disabled', 'defender-security' );
995 - $misc = array(
996 - 'outdated_period' => \WP_Defender\Behavior\Scan\Abandoned_Plugin::get_outdated_period(),
997 - 'labels' => $settings->labels(),
998 - );
1002 + $settings = new Scan_Settings();
1003 + $report = wd_di()->get( Malware_Report::class );
999 1004
1000 - // Todo: add logic for deactivated scan settings. Maybe display some notice.
1005 + $scan['isEnabledScanType'] = $this->service->is_any_scan_type_active();
1006 +
1001 1007 $data = array(
1002 - 'scan' => $scan,
1003 - 'settings' => $settings->export(),
1004 - 'report' => $report_text,
1005 - 'active_tools' => array(
1006 - 'integrity_check' => $settings->integrity_check,
1007 - 'check_known_vuln' => $settings->check_known_vuln,
1008 - 'scan_malware' => $settings->scan_malware,
1009 - 'check_abandoned_plugin' => $settings->check_abandoned_plugin,
1008 + 'scan' => $scan,
1009 + 'settings' => $settings->export(),
1010 + 'notification' => $report->to_string(),
1011 + 'misc' => array(
1012 + 'labels' => $settings->labels(),
1010 1013 ),
1011 - 'notification' => $report->to_string(),
1012 - 'next_run' => $report->get_next_run_as_string(),
1013 - 'misc' => $misc,
1014 - 'upsell' => array(
1015 - 'scan' => $this->get_scan_upsell( 'scan' ),
1016 - ),
1017 - 'hub_connector' => wd_di()->get( Hub_Connector::class )->data_frontend(),
1018 - 'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(),
1019 1014 );
1020 1015 $data['quarantine'] = $this->quarantine_controller->data_frontend();
1021 1016
1022 1017 return array_merge( $data, $this->dump_routes_and_nonces() );
@@ -1044,30 +1039,8 @@
1044 1039 }
1045 1040 }
1046 1041
1047 1042 /**
1048 - * Checks if any scan is active.
1049 - *
1050 - * @param bool $is_apikey Indicates if the API key is available for HC features.
1051 - *
1052 - * @return bool True if any scan is active, false otherwise.
1053 - */
1054 - private function is_any_active( bool $is_apikey ): bool {
1055 - $settings = new Scan_Settings();
1056 - $file_change_check = $settings->is_checked_any_file_change_types();
1057 -
1058 - if ( $is_apikey ) {
1059 - // HC version. Check all parent types.
1060 - return $file_change_check || $settings->check_known_vuln || $settings->scan_malware;
1061 - } else {
1062 - // Without HC access:
1063 - // Check the 'File change detection' type because only it's available with nested types.
1064 - // Check the Abandoned plugin type.
1065 - return $file_change_check || $settings->check_abandoned_plugin;
1066 - }
1067 - }
1068 -
1069 - /**
1070 1043 * Exports strings.
1071 1044 *
1072 1045 * @return array An array of strings.
1073 1046 */
@@ -1072,9 +1045,9 @@
1072 1045 * @return array An array of strings.
1073 1046 */
1074 1047 public function export_strings(): array {
1075 1048 $strings = array();
1076 - if ( $this->is_any_active( $this->is_apikey ) ) {
1049 + if ( $this->service->is_any_scan_type_active() ) {
1077 1050 $strings[] = esc_html__( 'Active', 'defender-security' );
1078 1051 } else {
1079 1052 $strings[] = esc_html__( 'Inactive', 'defender-security' );
1080 1053 }
@@ -1100,9 +1073,9 @@
1100 1073 * @return array Returns an array of configuration strings.
1101 1074 */
1102 1075 public function config_strings( array $config ): array {
1103 1076 $strings = array();
1104 - $strings[] = $this->service->is_any_scan_active( $config )
1077 + $strings[] = $this->service->check_scan_active_by( $config )
1105 1078 ? esc_html__( 'Active', 'defender-security' )
1106 1079 : esc_html__( 'Inactive', 'defender-security' );
1107 1080
1108 1081 if ( 'enabled' === $config['notification'] ) {
@@ -1107,9 +1080,10 @@
1107 1080
1108 1081 if ( 'enabled' === $config['notification'] ) {
1109 1082 $strings[] = esc_html__( 'Email notifications active', 'defender-security' );
1110 1083 }
1111 - if ( ! ( property_exists( $this, 'is_pro' ) ? $this->is_pro : wd_di()->get( WPMUDEV::class )->is_pro() ) ) {
1084 + // No Pro membership and No hosted site.
1085 + if ( ! $this->is_unlocked ) {
1112 1086 $strings[] = sprintf(
1113 1087 /* translators: %s: Html for Pro-tag. */
1114 1088 esc_html__( 'Scheduled scan inactive %s', 'defender-security' ),
1115 1089 '<span class="sui-tag sui-tag-pro">Pro</span>'