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/component/class-scan.php +234 -56 5.4.0 → 6.3.0 View file →
@@ -8,9 +8,8 @@
8 8 namespace WP_Defender\Component;
9 9
10 10 use Countable;
11 11 use ArrayIterator;
12 -use WP_Defender\Admin;
13 12 use WP_Defender\Component;
14 13 use WP_Plugins_List_Table;
15 14 use WP_Defender\Model\Scan_Item;
16 15 use WP_Defender\Behavior\WPMUDEV;
@@ -18,11 +17,10 @@
18 17 use WP_Defender\Behavior\Scan\Gather_Fact;
19 18 use WP_Defender\Behavior\Scan\Malware_Scan;
20 19 use WP_Defender\Behavior\Scan\Core_Integrity;
21 20 use WP_Defender\Behavior\Scan\Plugin_Integrity;
22 -use WP_Defender\Behavior\Scan\Malware_Deep_Scan;
23 -use WP_Defender\Behavior\Scan\Malware_Quick_Scan;
24 21 use WP_Defender\Behavior\Scan\Known_Vulnerability;
22 +use WP_Defender\Behavior\Scan\Abandoned_Plugin;
25 23 use WP_Defender\Model\Setting\Scan as Scan_Settings;
26 24 use WP_Defender\Helper\Analytics\Scan as Scan_Analytics;
27 25 use WP_Defender\Controller\Scan as Scan_Controller;
28 26
@@ -29,9 +27,13 @@
29 27 /**
30 28 * The Scan class handles the scanning process, managing tasks, and coordinating different types of scans.
31 29 */
32 30 class Scan extends Component {
31 + use \WP_Defender\Traits\Plugin;
33 32
33 + // For all Scan types where plugins are used.
34 + public const PLUGINS_ACTIONED = 'wp-defender-actioned-plugins';
35 +
34 36 /**
35 37 * The current scan model.
36 38 *
37 39 * @var Scan_Model
@@ -57,9 +59,8 @@
57 59 *
58 60 * @var Known_Vulnerability
59 61 */
60 62 private $known_vulnerability;
61 -
62 63 /**
63 64 * Instance of Malware_Scan to handle malware scanning.
64 65 *
65 66 * @var Malware_Scan
@@ -70,11 +71,18 @@
70 71 * Instance of Gather_Fact to gather necessary information before scanning.
71 72 *
72 73 * @var Gather_Fact|null
73 74 */
74 - private ?Gather_Fact $gather_fact;
75 + private ?Gather_Fact $gather_fact = null;
75 76
76 77 /**
78 + * Instance of Abandoned_Plugin to handle abandoned plugin checks.
79 + *
80 + * @var Abandoned_Plugin
81 + */
82 + private $abandoned_plugin;
83 +
84 + /**
77 85 * Lock file name for scanning.
78 86 *
79 87 * @var string
80 88 */
@@ -80,8 +88,15 @@
80 88 */
81 89 protected string $lock_filename = 'scan.lock';
82 90
83 91 /**
92 + * Is the Hub API key available?
93 + *
94 + * @var bool
95 + */
96 + private $is_apikey;
97 +
98 + /**
84 99 * Constructs the Scan object and initializes behaviors.
85 100 */
86 101 public function __construct() {
87 102 $this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
@@ -86,14 +101,17 @@
86 101 public function __construct() {
87 102 $this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
88 103 $this->attach_behavior( Core_Integrity::class, Core_Integrity::class );
89 104 $this->attach_behavior( Plugin_Integrity::class, Plugin_Integrity::class );
105 +
106 + $this->is_apikey = wd_di()->get( WPMUDEV::class )->is_apikey_available();
107 + $this->settings = wd_di()->get( Scan_Settings::class );
90 108 }
91 109
92 110 /**
93 111 * Performs additional actions after an advanced scan.
94 112 *
95 - * @param object $model The scan model.
113 + * @param Scan_Model $model The scan model.
96 114 */
97 115 public function advanced_scan_actions( $model ) {
98 116 $this->reindex_ignored_issues( $model );
99 117 $this->clean_up();
@@ -121,11 +139,13 @@
121 139 if ( Scan_Model::STATUS_INIT === $scan->status ) {
122 140 // Get the first.
123 141 $this->log( 'Prepare facts for a scan', Scan_Controller::SCAN_LOG );
124 142 $task = Scan_Model::STEP_GATHER_INFO;
143 + $this->scan->status = $task;
125 144 $this->scan->percent = 0;
126 145 $this->scan->total_tasks = $runner->count();
127 146 $this->scan->save();
147 + $this->scan->enqueue_status_message( $this->scan->get_status_text() );
128 148 }
129 149 if (
130 150 in_array(
131 151 $this->scan->status,
@@ -161,8 +181,9 @@
161 181 $this->scan->status = $runner->key();
162 182 $this->scan->task_checkpoint = '';
163 183 $this->scan->date_end = gmdate( 'Y-m-d H:i:s' );
164 184 $this->scan->save();
185 + $this->scan->enqueue_status_message( $this->scan->get_status_text() );
165 186 // Queue for next run.
166 187 return false;
167 188 }
168 189 $this->log( 'All done!', Scan_Controller::SCAN_LOG );
@@ -205,10 +226,9 @@
205 226 *
206 227 * @return array
207 228 */
208 229 public function get_tasks(): array {
209 - $this->settings = new Scan_Settings();
210 - $tasks = array( Scan_Model::STEP_GATHER_INFO => 'gather_info' );
230 + $tasks = array( Scan_Model::STEP_GATHER_INFO => 'gather_info' );
211 231 if ( $this->settings->integrity_check ) {
212 232 // Nested options.
213 233 if ( $this->settings->check_core ) {
214 234 $tasks[ Scan_Model::STEP_CHECK_CORE ] = 'core_integrity_check';
@@ -216,18 +236,21 @@
216 236 if ( $this->settings->check_plugins ) {
217 237 $tasks[ Scan_Model::STEP_CHECK_PLUGIN ] = 'plugin_integrity_check';
218 238 }
219 239 }
220 - if ( $this->is_pro() ) {
221 - if ( $this->settings->check_known_vuln ) {
222 - if ( $this->has_method( Scan_Model::STEP_VULN_CHECK ) ) {
223 - $tasks[ Scan_Model::STEP_VULN_CHECK ] = 'vuln_check';
224 - }
240 +
241 + if ( $this->settings->check_abandoned_plugin ) {
242 + $tasks[ Scan_Model::STEP_ABANDONED_PLUGIN_CHECK ] = 'abandoned_plugin_check';
243 + }
244 + // Check if this is HC feature.
245 + if ( $this->is_apikey ) {
246 + if ( $this->settings->check_known_vuln && $this->has_method( Scan_Model::STEP_VULN_CHECK ) ) {
247 + $tasks[ Scan_Model::STEP_VULN_CHECK ] = 'vuln_check';
225 248 }
226 - if ( $this->settings->scan_malware ) {
227 - if ( $this->has_method( Scan_Model::STEP_SUSPICIOUS_CHECK ) ) {
228 - $tasks[ Scan_Model::STEP_SUSPICIOUS_CHECK ] = 'suspicious_check';
229 - }
249 + // The division between PHP and JS files occurs when collecting files.
250 + if ( $this->settings->scan_malware && $this->has_method( Scan_Model::STEP_SUSPICIOUS_CHECK )
251 + ) {
252 + $tasks[ Scan_Model::STEP_SUSPICIOUS_CHECK ] = 'suspicious_check';
230 253 }
231 254 }
232 255
233 256 return $tasks;
@@ -242,9 +265,9 @@
242 265 */
243 266 private function task_handler( $task ) {
244 267 switch ( $task ) {
245 268 case 'gather_info':
246 - if ( empty( $this->gather_fact ) && class_exists( Gather_Fact::class ) ) {
269 + if ( ! ( $this->gather_fact instanceof Gather_Fact ) && class_exists( Gather_Fact::class ) ) {
247 270 $this->set_gather_fact(
248 271 wd_di()->make( Gather_Fact::class, array( 'scan' => $this->scan ) )
249 272 );
250 273 }
@@ -250,9 +273,9 @@
250 273 }
251 274
252 275 return $this->gather_info( $this->gather_fact );
253 276 case 'vuln_check':
254 - if ( empty( $this->known_vulnerability ) && class_exists( Known_Vulnerability::class ) ) {
277 + if ( ! ( $this->known_vulnerability instanceof Known_Vulnerability ) && class_exists( Known_Vulnerability::class ) ) {
255 278 $this->set_known_vulnerability(
256 279 wd_di()->make( Known_Vulnerability::class, array( 'scan' => $this->scan ) )
257 280 );
258 281 }
@@ -257,9 +280,8 @@
257 280 );
258 281 }
259 282
260 283 return $this->vuln_check( $this->known_vulnerability );
261 -
262 284 case 'suspicious_check':
263 285 if ( class_exists( Malware_Scan::class ) ) {
264 286 $this->set_malware_scan(
265 287 wd_di()->make( Malware_Scan::class, array( 'scan' => $this->scan ) )
@@ -266,11 +288,22 @@
266 288 );
267 289 }
268 290
269 291 return $this->suspicious_check( $this->malware_scan );
292 + case 'abandoned_plugin_check':
293 + if ( ! ( $this->abandoned_plugin instanceof Abandoned_Plugin ) && class_exists( Abandoned_Plugin::class ) ) {
294 + $this->set_abandoned_plugin(
295 + wd_di()->make( Abandoned_Plugin::class, array( 'scan' => $this->scan ) )
296 + );
297 + }
270 298
299 + return $this->abandoned_plugin_check( $this->abandoned_plugin );
300 + case 'core_integrity_check':
301 + return $this->core_integrity_check();
302 + case 'plugin_integrity_check':
303 + return $this->plugin_integrity_check();
271 304 default:
272 - return $this->$task();
305 + return false;
273 306 }
274 307 }
275 308
276 309 /**
@@ -299,9 +332,9 @@
299 332 }
300 333 }
301 334
302 335 /**
303 - * A wrapper method for Malware_Scan class method vuln_check.
336 + * A wrapper method for Malware_Scan class method suspicious_check.
304 337 *
305 338 * @param Malware_Scan $malware_scan An instance of Malware_Scan.
306 339 *
307 340 * @return bool True if method Malware_Scan::suspicious_check not exists else bool value returned by that method.
@@ -307,10 +340,10 @@
307 340 * @return bool True if method Malware_Scan::suspicious_check not exists else bool value returned by that method.
308 341 */
309 342 private function suspicious_check( Malware_Scan $malware_scan ): bool {
310 343 if ( method_exists( $malware_scan, 'suspicious_check' ) ) {
311 - $quick_scan = wd_di()->get( Malware_Quick_Scan::class );
312 - $deep_scan = wd_di()->get( Malware_Deep_Scan::class );
344 + $quick_scan = wd_di()->get( \WP_Defender\Behavior\Scan\Malware_Quick_Scan::class );
345 + $deep_scan = wd_di()->get( \WP_Defender\Behavior\Scan\Malware_Deep_Scan::class );
313 346
314 347 return $malware_scan->suspicious_check( $quick_scan, $deep_scan );
315 348 }
316 349
@@ -328,8 +361,34 @@
328 361 }
329 362 }
330 363
331 364 /**
365 + * Set the Abandoned_Plugin object.
366 + *
367 + * @param Abandoned_Plugin $abandoned_plugin The Abandoned_Plugin object to set.
368 + */
369 + public function set_abandoned_plugin( Abandoned_Plugin $abandoned_plugin ) {
370 + if ( class_exists( Abandoned_Plugin::class ) ) {
371 + $this->abandoned_plugin = $abandoned_plugin;
372 + }
373 + }
374 +
375 + /**
376 + * A wrapper method for Abandoned_Plugin class method abandoned_plugin_check.
377 + *
378 + * @param Abandoned_Plugin $abandoned_plugin An instance of Abandoned_Plugin.
379 + *
380 + * @return bool
381 + */
382 + private function abandoned_plugin_check( Abandoned_Plugin $abandoned_plugin ): bool {
383 + if ( method_exists( $abandoned_plugin, 'abandoned_plugin_check' ) ) {
384 + return $abandoned_plugin->abandoned_plugin_check();
385 + }
386 +
387 + return true;
388 + }
389 +
390 + /**
332 391 * Cancels an active scan and cleans up related data.
333 392 */
334 393 public function cancel_a_scan() {
335 394 $scan = Scan_Model::get_active();
@@ -336,9 +395,9 @@
336 395 if ( is_object( $scan ) ) {
337 396 $scan->delete();
338 397 }
339 398 $this->clean_up();
340 - $this->remove_lock();
399 + $this->remove_lock( $this->lock_filename );
341 400
342 401 $scan_analytics = wd_di()->get( Scan_Analytics::class );
343 402
344 403 $scan_analytics->track_feature(
@@ -377,9 +436,9 @@
377 436 public function clean_up() {
378 437 $this->delete_interim_data();
379 438
380 439 $models = Scan_Model::get_last_all();
381 - if ( ! empty( $models ) ) {
440 + if ( is_array( $models ) && array() !== $models ) {
382 441 // Remove the latest. Don't remove code to find the first value.
383 442 $current = array_shift( $models );
384 443 foreach ( $models as $model ) {
385 444 $model->delete();
@@ -403,23 +462,44 @@
403 462 return $count;
404 463 }
405 464
406 465 /**
466 + * Checks if any scan type is active.
467 + *
468 + * @return bool True if any scan type is active, false otherwise.
469 + */
470 + public function is_any_scan_type_active(): bool {
471 + $settings = $this->settings;
472 + $file_change_check = $settings->is_checked_any_file_change_types();
473 + // Check#1 the 'File change detection' type because only it's available with nested types.
474 + // Check#2 the Abandoned plugin type.
475 + $check_free_settings = $file_change_check || $settings->check_abandoned_plugin;
476 + // Indicates if the API key is available for HC features.
477 + if ( $this->is_apikey ) {
478 + // HC version. Check all parent types.
479 + return $check_free_settings || $settings->check_known_vuln || $settings->scan_malware;
480 + } else {
481 + // Without HC access.
482 + return $check_free_settings;
483 + }
484 + }
485 +
486 +
487 + /**
407 488 * Checks if any scan type is active based on the scan settings and the user's membership status.
408 489 *
409 490 * @param array $scan_settings The scan settings.
410 - * @param bool $is_pro Whether the user has a pro membership.
411 491 *
412 492 * @return bool Returns true if any scan type is active, false otherwise.
413 493 */
414 - public function is_any_scan_active( $scan_settings, $is_pro ): bool {
415 - if ( empty( $scan_settings['integrity_check'] ) ) {
494 + public function check_scan_active_by( $scan_settings ): bool {
495 + if ( ! isset( $scan_settings['integrity_check'] ) || ! $scan_settings['integrity_check'] ) {
416 496 // Check the parent type.
417 497 $file_change_check = false;
418 498 } elseif (
419 - ! empty( $scan_settings['integrity_check'] )
420 - && empty( $scan_settings['check_core'] )
421 - && empty( $scan_settings['check_plugins'] )
499 + $scan_settings['integrity_check']
500 + && ( ! isset( $scan_settings['check_core'] ) || ! $scan_settings['check_core'] )
501 + && ( ! isset( $scan_settings['check_plugins'] ) || ! $scan_settings['check_plugins'] )
422 502 ) {
423 503 // Check the parent and child types.
424 504 $file_change_check = false;
425 505 } else {
@@ -424,16 +504,19 @@
424 504 $file_change_check = false;
425 505 } else {
426 506 $file_change_check = true;
427 507 }
428 - // Similar to is_any_active(...) method from the controller.
429 - if ( $is_pro ) {
430 - // Pro version. Check all parent types.
431 - return $file_change_check || ! empty( $scan_settings['check_known_vuln'] ) || ! empty( $scan_settings['scan_malware'] );
432 - } else {
433 - // Free version. Check the 'File change detection' type because only it's available with nested types.
434 - return $file_change_check;
508 +
509 + $check_free_settings = $file_change_check
510 + || ( isset( $scan_settings['check_abandoned_plugin'] ) && $scan_settings['check_abandoned_plugin'] );
511 + // For HC features.
512 + if ( $this->is_apikey ) {
513 + // Similar to is_any_scan_type_active() method.Check all parent types including HC features.
514 + return $check_free_settings || ( isset( $scan_settings['check_known_vuln'] ) && $scan_settings['check_known_vuln'] )
515 + || ( isset( $scan_settings['scan_malware'] ) && $scan_settings['scan_malware'] );
435 516 }
517 +
518 + return $check_free_settings;
436 519 }
437 520
438 521 /**
439 522 * Update the idle scan status due the time limit.
@@ -449,15 +532,17 @@
449 532 $ready_to_send = true;
450 533 }
451 534 $this->delete_interim_data();
452 535
453 - as_unschedule_all_actions( 'defender/async_scan' );
536 + if ( function_exists( 'as_unschedule_all_actions' ) ) {
537 + as_unschedule_all_actions( 'defender/async_scan' );
538 + }
454 539
455 540 $idle_scan->status = Scan_Model::STATUS_IDLE;
456 541 $idle_scan->task_checkpoint = 'time_limit';
457 542 $idle_scan->save();
458 543
459 - $this->remove_lock();
544 + $this->remove_lock( $this->lock_filename );
460 545 if ( $ready_to_send ) {
461 546 do_action( 'defender_notify', 'malware-notification', $idle_scan );
462 547 }
463 548 }
@@ -476,15 +561,17 @@
476 561 $ready_to_send = true;
477 562 }
478 563 $this->delete_interim_data();
479 564
480 - as_unschedule_all_actions( 'defender/async_scan' );
565 + if ( function_exists( 'as_unschedule_all_actions' ) ) {
566 + as_unschedule_all_actions( 'defender/async_scan' );
567 + }
481 568
482 569 $scan->status = Scan_Model::STATUS_IDLE;
483 570 $scan->task_checkpoint = 'checksum_issue';
484 571 $scan->save();
485 572
486 - $this->remove_lock();
573 + $this->remove_lock( $this->lock_filename );
487 574 if ( $ready_to_send ) {
488 575 do_action( 'defender_notify', 'malware-notification', $scan );
489 576 }
490 577 }
@@ -499,9 +586,11 @@
499 586 delete_site_option( Gather_Fact::CACHE_CONTENT );
500 587 delete_site_option( Malware_Scan::YARA_RULES );
501 588 delete_site_option( Core_Integrity::CACHE_CHECKSUMS );
502 589 delete_site_option( Plugin_Integrity::PLUGIN_SLUGS );
590 + delete_site_option( Plugin_Integrity::PLUGIN_SLUGS_CHECKPOINT );
503 591 delete_site_option( Plugin_Integrity::PLUGIN_PREMIUM_SLUGS );
592 + delete_site_option( self::PLUGINS_ACTIONED );
504 593 $this->maybe_track_failed_checksum();
505 594 }
506 595
507 596 /**
@@ -532,20 +621,20 @@
532 621 'WP_Plugins_List_Table',
533 622 array( 'screen' => get_current_screen() )
534 623 );
535 624 $bugs = $this->vulnerability_details[ $file ]['bugs'];
536 - if ( empty( $bugs ) ) {
625 + if ( ! is_array( $bugs ) || array() === $bugs ) {
537 626 return;
538 627 }
539 628 $last_fixed_in = '0';
540 629 // Check if there have been updates since the last scan.
541 630 $exist_update = true;
542 - if ( isset( $plugin_data['Version'] ) && ! empty( $plugin_data['Version'] ) ) {
631 + if ( isset( $plugin_data['Version'] ) && '' !== $plugin_data['Version'] ) {
543 632 // The current plugin version.
544 633 $current_version = $plugin_data['Version'];
545 634 foreach ( $bugs as $bug_details ) {
546 635 // If the fixed version is existed then get the latest one.
547 - if ( isset( $bug_details['fixed_in'] ) && ! empty( $bug_details['fixed_in'] )
636 + if ( isset( $bug_details['fixed_in'] ) && '' !== $bug_details['fixed_in']
548 637 && version_compare( $bug_details['fixed_in'], $last_fixed_in, '>' )
549 638 ) {
550 639 $last_fixed_in = $bug_details['fixed_in'];
551 640 }
@@ -557,9 +646,9 @@
557 646 // If there were updates, do not display notice.
558 647 if ( $exist_update ) {
559 648 return;
560 649 }
561 - if ( empty( $plugin_data['slug'] ) && isset( $this->vulnerability_details[ $file ]['base_slug'] ) ) {
650 + if ( ( ! isset( $plugin_data['slug'] ) || '' === $plugin_data['slug'] ) && isset( $this->vulnerability_details[ $file ]['base_slug'] ) ) {
562 651 $plugin_data['slug'] = $this->vulnerability_details[ $file ]['base_slug'];
563 652 }
564 653
565 654 if ( is_network_admin() || ! is_multisite() ) {
@@ -579,9 +668,9 @@
579 668 'notice-error'
580 669 );
581 670
582 671 $notice = sprintf(
583 - /* translators: %s - Plugin name. */
672 + /* translators: %s: Plugin name. */
584 673 esc_html__(
585 674 '%s has detected a vulnerability in this plugin that may cause harm to your site.',
586 675 'defender-security'
587 676 ),
@@ -596,9 +685,9 @@
596 685 $notice .= implode( '<br/>', $lines );
597 686 $notice .= '<hr/><span class="vulnerability-indent"></span>';
598 687 if ( '0' !== $last_fixed_in ) {
599 688 $notice .= sprintf(
600 - /* translators: %s - Version number. */
689 + /* translators: %s: Version number. */
601 690 esc_html__(
602 691 'The vulnerability has been fixed in version %s. We recommend that you update this plugin accordingly.',
603 692 'defender-security'
604 693 ),
@@ -611,15 +700,15 @@
611 700 );
612 701 }
613 702 } else {
614 703 $notice .= '<br/><span class="vulnerability-indent"></span>' . $bugs[0]['title'] . '<br/><span class="vulnerability-indent"></span>';
615 - $notice .= empty( $last_fixed_in )
704 + $notice .= '0' === $last_fixed_in
616 705 ? esc_html__(
617 706 'We recommend that you deactivate this plugin until the vulnerability has been fixed.',
618 707 'defender-security'
619 708 )
620 709 : sprintf(
621 - /* translators: 1: Version number. */
710 + /* translators: %s: Version number. */
622 711 esc_html__(
623 712 'The vulnerability has been fixed in version %s. We recommend that you update this plugin accordingly.',
624 713 'defender-security'
625 714 ),
@@ -643,9 +732,9 @@
643 732
644 733 $last = Scan_Model::get_last();
645 734 if ( is_object( $last ) && ! is_wp_error( $last ) ) {
646 735 $vulnerability_issues = $last->get_issues( Scan_Item::TYPE_VULNERABILITY );
647 - if ( empty( $vulnerability_issues ) ) {
736 + if ( ! is_array( $vulnerability_issues ) || array() === $vulnerability_issues ) {
648 737 return;
649 738 }
650 739
651 740 add_action( 'admin_print_styles-plugins.php', array( $this, 'show_plugin_admin_styles' ) );
@@ -674,12 +763,12 @@
674 763 */
675 764 public static function clear_logs() {
676 765 global $wpdb;
677 766
678 - $table_actions = ! empty( $wpdb->actionscheduler_actions ) ?
767 + $table_actions = isset( $wpdb->actionscheduler_actions ) && '' !== $wpdb->actionscheduler_actions ?
679 768 $wpdb->actionscheduler_actions :
680 769 $wpdb->prefix . 'actionscheduler_actions';
681 - $table_logs = ! empty( $wpdb->actionscheduler_logs ) ?
770 + $table_logs = isset( $wpdb->actionscheduler_logs ) && '' !== $wpdb->actionscheduler_logs ?
682 771 $wpdb->actionscheduler_logs :
683 772 $wpdb->prefix . 'actionscheduler_logs';
684 773
685 774 $table_count = (int) $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
@@ -708,9 +797,9 @@
708 797 $limit
709 798 )
710 799 );
711 800 while ( $action_ids ) {
712 - if ( empty( $action_ids ) ) {
801 + if ( ! is_array( $action_ids ) || array() === $action_ids ) {
713 802 break;
714 803 }
715 804
716 805 $query = "DELETE as_actions, as_logs FROM {$table_actions} as_actions LEFT JOIN {$table_logs} as_logs ON as_actions.action_id = as_logs.action_id WHERE as_actions.action_id IN ( " . implode(
@@ -770,9 +859,9 @@
770 859 return true;
771 860 }
772 861
773 862 /**
774 - * Gey intentions.
863 + * Get intentions.
775 864 *
776 865 * @since 4.11.0
777 866 * @return array
778 867 */
@@ -783,6 +872,95 @@
783 872 'delete',
784 873 'unignore',
785 874 'quarantine',
786 875 );
876 + }
877 +
878 + /**
879 + * Get the list of actioned plugins taking into account the Ignored and Excluded.
880 + *
881 + * @return array
882 + */
883 + public function gather_actioned_plugin_details(): array {
884 + $cache = get_site_option( self::PLUGINS_ACTIONED );
885 + if ( self::are_actioned_plugins( $cache ) ) {
886 + return $cache;
887 + }
888 +
889 + $items = array();
890 + $is_plugin_used = false;
891 + if ( $this->settings->integrity_check && $this->settings->check_plugins ) {
892 + $is_plugin_used = true;
893 + } elseif ( $this->settings->check_abandoned_plugin ) {
894 + $is_plugin_used = true;
895 + } elseif ( $this->is_apikey && ( $this->settings->check_known_vuln || $this->settings->scan_malware ) ) {
896 + $is_plugin_used = true;
897 + }
898 +
899 + if ( $is_plugin_used ) {
900 + $model = Scan_Model::get_last();
901 + /**
902 + * Exclude plugin slugs.
903 + *
904 + * @param array $slugs Slugs of excluded plugins.
905 + *
906 + * @since 3.1.0
907 + */
908 + $excluded_slugs = apply_filters( 'wd_scan_excluded_plugin_slugs', array() );
909 + $excluded_slugs = ! is_array( $excluded_slugs ) ? (array) $excluded_slugs : $excluded_slugs;
910 +
911 + foreach ( $this->get_plugins() as $slug => $item ) {
912 + if ( is_object( $model ) && $model->is_issue_ignored( $slug ) ) {
913 + continue;
914 + }
915 + $base_slug = $this->get_plugin_slug_by( $slug );
916 + if ( in_array( $base_slug, $excluded_slugs, true ) ) {
917 + continue;
918 + }
919 + // Use keys with the first capital letter to match default plugin header keys.
920 + $items[ $base_slug ] = array(
921 + 'Name' => $item['Name'],
922 + 'Version' => $item['Version'],
923 + 'Slug' => $slug,
924 + );
925 + }
926 +
927 + update_site_option( self::PLUGINS_ACTIONED, $items );
928 + }
929 +
930 + return $items;
931 + }
932 +
933 + /**
934 + * Does the list of actioned plugins exist and no empty?
935 + *
936 + * @param array|false $actioned_plugins The actioned plugins.
937 + *
938 + * @return bool
939 + */
940 + public static function are_actioned_plugins( $actioned_plugins ): bool {
941 + return is_array( $actioned_plugins ) && array() !== $actioned_plugins;
942 + }
943 +
944 + /**
945 + * Get the lock filename.
946 + *
947 + * @return string
948 + */
949 + public function get_lock_filename(): string {
950 + return $this->lock_filename;
951 + }
952 +
953 + /**
954 + * Get the text for the emergency scan stop case.
955 + *
956 + * @return string
957 + */
958 + public static function get_emergency_scan_stop_text(): string {
959 + $text = __( 'Scan aborted. No scan types are enabled. Enable at least one scan type under Defender > Settings > Tools before running a scan.', 'defender-security' );
960 + if ( defender_is_wp_cli() ) {
961 + return $text;
962 + }
963 +
964 + return esc_html( $text );
787 965 }
788 966 }