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-cli.php +233 -47 6.1.0 → 6.3.0 View file →
@@ -18,8 +18,10 @@
18 18 use WP_Defender\Traits\IO;
19 19 use WP_Defender\Traits\Theme;
20 20 use WP_Defender\Traits\Plugin;
21 21 use WP_Defender\Traits\Formats;
22 +use WP_Defender\Behavior\WPMUDEV;
23 +use WP_Defender\Component\Audit;
22 24 use WP_Defender\Model\Audit_Log;
23 25 use WP_Defender\Model\Scan_Item;
24 26 use WP_Defender\Model\Lockout_Ip;
25 27 use WP_Defender\Model\Lockout_Log;
@@ -62,22 +64,32 @@
62 64 moment_datetime_format_from as protected;
63 65 persistent_hub_datetime_format as protected;
64 66 time_since as protected;
65 67 get_local_human_date as protected;
68 + get_time_diff as protected;
69 + }
70 + use IO {
71 + try_create_lock as protected;
66 72 release_cron_lock as protected;
67 - create_lock as protected;
68 - has_lock as protected;
69 73 remove_lock as protected;
70 - check_plugin_on_wp_org as protected;
71 - check_by_readme_file as protected;
72 74 acquire_cron_lock as protected;
73 75 compare_hashes as protected;
74 76 delete_dir as protected;
75 77 detect_line_ending as protected;
78 + get_log_path as protected;
79 + }
80 + use Theme {
81 + get_path_of_themes_dir as protected;
82 + get_theme as protected;
83 + get_theme_slugs as protected;
84 + get_themes as protected;
85 + is_active_theme as protected;
86 + }
87 + use Plugin {
88 + check_plugin_on_wp_org as protected;
89 + check_by_readme_file as protected;
76 90 get_abs_plugin_path_by_slug as protected;
77 91 get_active_plugin_names as protected;
78 - get_path_of_themes_dir as protected;
79 - get_log_path as protected;
80 92 get_plugin_details_by as protected;
81 93 get_plugin_directory_name as protected;
82 94 get_plugin_headers as protected;
83 95 get_plugin_relative_path as protected;
@@ -83,21 +95,13 @@
83 95 get_plugin_relative_path as protected;
84 96 get_plugin_slugs as protected;
85 97 get_plugins as protected;
86 98 get_plugin_slug_by as protected;
87 - get_theme as protected;
88 - get_theme_slugs as protected;
89 - get_themes as protected;
90 - get_time_diff as protected;
91 99 handle_wp_org_response_by as protected;
92 100 is_active_plugin as protected;
93 - is_active_theme as protected;
94 101 is_likely_wporg_slug as protected;
95 102 ping_wp_org_by_plugin_slug as protected;
96 103 }
97 - use IO;
98 - use Theme;
99 - use Plugin;
100 104
101 105 /**
102 106 * Run scans and manage scan results via WP-CLI.
103 107 *
@@ -199,8 +203,12 @@
199 203 default:
200 204 WP_CLI::error( sprintf( 'Unknown scan type %s', $type ) );
201 205 break;
202 206 }
207 + $scan_component = wd_di()->get( Scan_Component::class );
208 + if ( ! $scan_component->is_any_scan_type_active() ) {
209 + WP_CLI::error( Scan_Component::get_emergency_scan_stop_text() );
210 + }
203 211 WP_CLI::log( 'Check if there is a scan ongoing...' );
204 212 $scan = Model_Scan::get_active();
205 213 if ( ! is_object( $scan ) ) {
206 214 WP_CLI::log( 'No active scan, creating...' );
@@ -210,9 +218,9 @@
210 218 $scan = Model_Scan::create();
211 219 if ( is_wp_error( $scan ) ) {
212 220 WP_CLI::error( $scan->get_error_message() );
213 221 }
214 - wd_di()->get( Scan_Component::class )->gather_actioned_plugin_details();
222 + $scan_component->gather_actioned_plugin_details();
215 223 } else {
216 224 WP_CLI::log( 'Continue from last scan' );
217 225 }
218 226 // Start detailed scan.
@@ -219,9 +227,9 @@
219 227 if ( $is_detailed ) {
220 228 $start = microtime( true );
221 229 }
222 230 $handler = wd_di()->get( Scan_Component::class );
223 - while ( $handler->process() === false ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedWhile
231 + while ( $handler->process() === false ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedWhile
224 232 }
225 233 $scan = Model_Scan::get_last();
226 234 if ( ! is_object( $scan ) || is_wp_error( $scan ) ) {
227 235 return;
@@ -496,19 +504,38 @@
496 504 WP_CLI::error( 'Invalid command' );
497 505
498 506 return;
499 507 }
500 - [$command] = $args;
508 + if ( ! $this->is_testing_mode() ) {
509 + return;
510 + }
511 +
512 + [ $command ] = $args;
501 513 switch ( $command ) {
502 514 case 'scan:core':
515 + WP_CLI::confirm( 'This will modify a WordPress core file (wp-load.php). Are you sure?', array() );
516 +
503 517 $file_path = ABSPATH . 'wp-load.php';
504 - $content = '//this make different';
505 - if ( $wp_filesystem->exists( $file_path ) ) {
506 - $content = $wp_filesystem->get_contents( $file_path ) . $content;
518 + if ( ! $wp_filesystem->exists( $file_path ) ) {
519 + WP_CLI::error( sprintf( 'File does not exist: %s', $file_path ) );
520 +
521 + return;
507 522 }
508 - $wp_filesystem->put_contents( $file_path, $content );
523 + $content = $wp_filesystem->get_contents( $file_path );
524 + if ( false === $content ) {
525 + WP_CLI::error( sprintf( 'Could not read file: %s', $file_path ) );
526 +
527 + return;
528 + }
529 + if ( str_contains( $content, '//this make different' ) ) {
530 + WP_CLI::warning( 'File already seeded, skipping.' );
531 +
532 + return;
533 + }
534 + $wp_filesystem->put_contents( $file_path, $content . '//this make different' );
509 535 break;
510 536 case 'ip:logs':
537 + WP_CLI::confirm( 'This will insert fake firewall lockout log entries into the database. Are you sure?', array() );
511 538 // We will generate randomly 10k logs in 3 months.
512 539 $types = array( Lockout_Log::AUTH_FAIL, Lockout_Log::AUTH_LOCK, Lockout_Log::ERROR_404, Lockout_Log::LOCKOUT_404, Lockout_Log::LOCKOUT_UA );
513 540 $is_lock = array( Lockout_Log::AUTH_LOCK, Lockout_Log::LOCKOUT_404, Lockout_Log::LOCKOUT_UA );
514 541 $faker = Factory::create();
@@ -584,15 +611,38 @@
584 611 WP_CLI::error( 'Invalid command' );
585 612
586 613 return;
587 614 }
588 - [$command] = $args;
615 + if ( ! $this->is_testing_mode() ) {
616 + return;
617 + }
618 +
619 + [ $command ] = $args;
589 620 switch ( $command ) {
590 621 case 'scan:core':
591 - $content = file_get_contents( ABSPATH . 'wp-load.php' );
592 - $wp_filesystem->put_contents( ABSPATH . 'wp-load.php', str_replace( '//this make different', '', $content ) );
622 + WP_CLI::confirm( 'This will revert the modification to wp-load.php. Are you sure?', array() );
623 +
624 + $file_path = ABSPATH . 'wp-load.php';
625 + if ( ! $wp_filesystem->exists( $file_path ) ) {
626 + WP_CLI::error( sprintf( 'File does not exist: %s', $file_path ) );
627 +
628 + return;
629 + }
630 + $content = $wp_filesystem->get_contents( $file_path );
631 + if ( false === $content ) {
632 + WP_CLI::error( sprintf( 'Could not read file: %s', $file_path ) );
633 +
634 + return;
635 + }
636 + if ( ! str_contains( $content, '//this make different' ) ) {
637 + WP_CLI::warning( 'Marker not found in file, nothing to revert.' );
638 +
639 + return;
640 + }
641 + $wp_filesystem->put_contents( $file_path, str_replace( '//this make different', '', $content ) );
593 642 break;
594 643 case 'scan:suspicious':
644 + WP_CLI::confirm( 'This will delete the false-positive test file. Are you sure?', array() );
595 645 wp_delete_file( WP_CONTENT_DIR . '/false-positive.php' );
596 646 break;
597 647 default:
598 648 break;
@@ -599,9 +649,9 @@
599 649 }
600 650 }
601 651
602 652 /**
603 - * Manage audit logs via WP-CLI. Requires a WPMU DEV subscription.
653 + * Manage audit logs via WP-CLI.
604 654 *
605 655 * ## OPTIONS
606 656 *
607 657 * <command>
@@ -608,8 +658,9 @@
608 658 * : Action to perform.
609 659 * ---
610 660 * options:
611 661 * - reset
662 + * - sync
612 663 * ---
613 664 *
614 665 * ## EXAMPLES
615 666 *
@@ -616,15 +667,46 @@
616 667 * # Delete all audit log entries from the database.
617 668 * $ wp defender audit reset
618 669 * All clear
619 670 *
671 + * # Synchronize local audit logs with cloud history (Pro only).
672 + * $ wp defender audit sync
673 + * Sync completed.
674 + *
620 675 * @param mixed $args Command arguments.
621 676 */
622 677 public function audit( $args ) {
623 - if ( defender_is_wp_org_version() ) {
624 - WP_CLI::warning( 'A WPMU DEV subscription is required to use this command.' );
678 + if ( ! is_array( $args ) || array() === $args ) {
679 + WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
680 + WP_CLI::runcommand(
681 + 'defender audit --help',
682 + array(
683 + 'launch' => false,
684 + 'exit_error' => false,
685 + )
686 + );
687 +
625 688 return;
626 689 }
690 +
691 + [$command] = $args;
692 + switch ( $command ) {
693 + case 'reset':
694 + wd_di()->get( Audit::class )->reset();
695 +
696 + WP_CLI::log( 'All clear' );
697 + break;
698 + default:
699 + WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
700 + WP_CLI::runcommand(
701 + 'defender audit --help',
702 + array(
703 + 'launch' => false,
704 + 'exit_error' => false,
705 + )
706 + );
707 + break;
708 + }
627 709 }
628 710
629 711 /**
630 712 * Manage security headers via WP-CLI.
@@ -663,9 +745,9 @@
663 745 WP_CLI::error( 'Invalid command.' );
664 746
665 747 return;
666 748 }
667 - $model = new Security_Headers();
749 + $model = new Security_Headers();
668 750 [$command] = $args;
669 751 switch ( $command ) {
670 752 case 'check':
671 753 $i = 1;
@@ -749,9 +831,15 @@
749 831 */
750 832 public function settings( $args, $options ) {
751 833 if ( ! is_array( $args ) || array() === $args ) {
752 834 WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
753 - WP_CLI::runcommand( 'defender settings --help', [ 'launch' => false, 'exit_error' => false ] );
835 + WP_CLI::runcommand(
836 + 'defender settings --help',
837 + array(
838 + 'launch' => false,
839 + 'exit_error' => false,
840 + )
841 + );
754 842
755 843 return;
756 844 }
757 845
@@ -760,8 +848,9 @@
760 848 case 'reset':
761 849 WP_CLI::confirm( 'This will completely reset the plugin settings, are you sure to continue?', $options );
762 850 // Analog Settings > Reset Settings.
763 851 wd_di()->get( Login_Access::class )->remove_settings();
852 + wd_di()->get( Audit_Logging::class )->remove_settings();
764 853 wd_di()->get( Dashboard::class )->remove_settings();
765 854 wd_di()->get( Security_Tweaks::class )->remove_settings();
766 855 wd_di()->get( \WP_Defender\Controller\Scan::class )->remove_settings();
767 856 // Parent and submodules.
@@ -774,9 +863,15 @@
774 863 WP_CLI::log( 'All cleared!' );
775 864 break;
776 865 default:
777 866 WP_CLI::error( sprintf( 'Unknown command %s, use correct arguments. See below...', $command ), false );
778 - WP_CLI::runcommand( 'defender settings --help', [ 'launch' => false, 'exit_error' => false ] );
867 + WP_CLI::runcommand(
868 + 'defender settings --help',
869 + array(
870 + 'launch' => false,
871 + 'exit_error' => false,
872 + )
873 + );
779 874 break;
780 875 }
781 876 }
782 877
@@ -832,9 +927,15 @@
832 927 public function firewall( $args, $options ) {
833 928 $arg_count = is_array( $args ) || $args instanceof Countable ? count( $args ) : 0;
834 929 if ( $arg_count < 2 ) {
835 930 WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
836 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
931 + WP_CLI::runcommand(
932 + 'defender firewall --help',
933 + array(
934 + 'launch' => false,
935 + 'exit_error' => false,
936 + )
937 + );
837 938
838 939 return;
839 940 }
840 941
@@ -844,9 +945,15 @@
844 945 $field = $args[2] ?? ( 'list' === $command ? 'all' : '' );
845 946
846 947 if ( ! is_string( $type ) || '' === $type ) {
847 948 WP_CLI::error( 'Invalid option.', false );
848 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
949 + WP_CLI::runcommand(
950 + 'defender firewall --help',
951 + array(
952 + 'launch' => false,
953 + 'exit_error' => false,
954 + )
955 + );
849 956
850 957 return;
851 958 }
852 959 switch ( $command ) {
@@ -882,9 +989,15 @@
882 989 $field_default = array( 'blocklist', 'allowlist', 'country_allowlist', 'country_blocklist', 'license_key' );
883 990
884 991 if ( ! in_array( $type, $type_default, true ) ) {
885 992 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $type ), false );
886 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
993 + WP_CLI::runcommand(
994 + 'defender firewall --help',
995 + array(
996 + 'launch' => false,
997 + 'exit_error' => false,
998 + )
999 + );
887 1000
888 1001 return;
889 1002 }
890 1003
@@ -889,9 +1002,15 @@
889 1002 }
890 1003
891 1004 if ( ! in_array( $field, $field_default, true ) ) {
892 1005 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $field ), false );
893 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1006 + WP_CLI::runcommand(
1007 + 'defender firewall --help',
1008 + array(
1009 + 'launch' => false,
1010 + 'exit_error' => false,
1011 + )
1012 + );
894 1013
895 1014 return;
896 1015 }
897 1016
@@ -925,9 +1044,9 @@
925 1044 $model->save();
926 1045 } elseif ( 'maxmind' === $type ) {
927 1046 try {
928 1047 $model = wd_di()->get( \WP_Defender\Model\Setting\Blacklist_Lockout::class );
929 - if ( ! is_null( $model->geodb_path ) && is_file( $model->geodb_path ) ) {
1048 + if ( is_string( $model->geodb_path ) && is_file( $model->geodb_path ) ) {
930 1049 wp_delete_file( $model->geodb_path );
931 1050 }
932 1051 $model->maxmind_license_key = '';
933 1052 $model->geodb_path = '';
@@ -978,9 +1097,15 @@
978 1097 $field_default = array( 'lockout' );
979 1098
980 1099 if ( ! in_array( $type, $type_default, true ) ) {
981 1100 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $type ), false );
982 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1101 + WP_CLI::runcommand(
1102 + 'defender firewall --help',
1103 + array(
1104 + 'launch' => false,
1105 + 'exit_error' => false,
1106 + )
1107 + );
983 1108
984 1109 return;
985 1110 }
986 1111
@@ -985,9 +1110,15 @@
985 1110 }
986 1111
987 1112 if ( ! in_array( $field, $field_default, true ) ) {
988 1113 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $field ), false );
989 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1114 + WP_CLI::runcommand(
1115 + 'defender firewall --help',
1116 + array(
1117 + 'launch' => false,
1118 + 'exit_error' => false,
1119 + )
1120 + );
990 1121
991 1122 return;
992 1123 }
993 1124
@@ -1000,9 +1131,15 @@
1000 1131 $model->save();
1001 1132 }
1002 1133 } else {
1003 1134 WP_CLI::error( 'Option \'ips\' is not provided. See below...', false );
1004 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1135 + WP_CLI::runcommand(
1136 + 'defender firewall --help',
1137 + array(
1138 + 'launch' => false,
1139 + 'exit_error' => false,
1140 + )
1141 + );
1005 1142
1006 1143 return;
1007 1144 }
1008 1145
@@ -1022,15 +1159,27 @@
1022 1159 $type_default = array( 'user_agent' );
1023 1160 $field_default = array( 'all', 'allowlist', 'blocklist' );
1024 1161 if ( ! in_array( $type, $type_default, true ) ) {
1025 1162 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $type ), false );
1026 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1163 + WP_CLI::runcommand(
1164 + 'defender firewall --help',
1165 + array(
1166 + 'launch' => false,
1167 + 'exit_error' => false,
1168 + )
1169 + );
1027 1170
1028 1171 return;
1029 1172 }
1030 1173 if ( ! in_array( $field, $field_default, true ) ) {
1031 1174 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $field ), false );
1032 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1175 + WP_CLI::runcommand(
1176 + 'defender firewall --help',
1177 + array(
1178 + 'launch' => false,
1179 + 'exit_error' => false,
1180 + )
1181 + );
1033 1182
1034 1183 return;
1035 1184 }
1036 1185 $model = wd_di()->get( User_Agent_Lockout::class );
@@ -1060,15 +1209,27 @@
1060 1209 */
1061 1210 private function toggle_firewall_submodule( $key_word, $submodule, $action ) {
1062 1211 if ( 'submodule' !== $key_word ) {
1063 1212 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $key_word ), false );
1064 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1213 + WP_CLI::runcommand(
1214 + 'defender firewall --help',
1215 + array(
1216 + 'launch' => false,
1217 + 'exit_error' => false,
1218 + )
1219 + );
1065 1220
1066 1221 return;
1067 1222 }
1068 1223 if ( ! in_array( $submodule, array( 'login_protection', '404_detection', 'user_agent' ), true ) ) {
1069 1224 WP_CLI::error( sprintf( 'Invalid option %s. See below...', $submodule ), false );
1070 - WP_CLI::runcommand( 'defender firewall --help', [ 'launch' => false, 'exit_error' => false ] );
1225 + WP_CLI::runcommand(
1226 + 'defender firewall --help',
1227 + array(
1228 + 'launch' => false,
1229 + 'exit_error' => false,
1230 + )
1231 + );
1071 1232
1072 1233 return;
1073 1234 }
1074 1235 // Get submodule slug.
@@ -1102,12 +1263,25 @@
1102 1263 WP_CLI::success( sprintf( 'Firewall "%s" has been %s.', $submodule, $text ) );
1103 1264 }
1104 1265
1105 1266 /**
1106 - * Manage mask login settings via WP-CLI.
1267 + * Check if the testing mode is enabled.
1268 + * Outputs an error and returns false if WP_DEFENDER_TESTING is not defined and true.
1107 1269 *
1108 - * ## OPTIONS
1109 - *
1270 + * @return bool
1271 + */
1272 + private function is_testing_mode(): bool {
1273 + if ( ! defined( 'WP_DEFENDER_TESTING' ) || ! WP_DEFENDER_TESTING ) {
1274 + WP_CLI::error( 'This command is intended for testing only. Define WP_DEFENDER_TESTING as true to proceed.' );
1275 +
1276 + return false;
1277 + }
1278 +
1279 + return true;
1280 + }
1281 +
1282 + /**
1283 + * Force Bulk Password Reset.
1110 1284 * <command>
1111 1285 * : Action to perform.
1112 1286 * ---
1113 1287 * options:
@@ -1124,9 +1298,15 @@
1124 1298 */
1125 1299 public function mask_login( $args ) {
1126 1300 if ( ( is_array( $args ) || $args instanceof Countable ? count( $args ) : 0 ) < 1 ) {
1127 1301 WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
1128 - WP_CLI::runcommand( 'defender mask_login --help', [ 'launch' => false, 'exit_error' => false ] );
1302 + WP_CLI::runcommand(
1303 + 'defender mask_login --help',
1304 + array(
1305 + 'launch' => false,
1306 + 'exit_error' => false,
1307 + )
1308 + );
1129 1309
1130 1310 return;
1131 1311 }
1132 1312
@@ -1218,9 +1398,15 @@
1218 1398 */
1219 1399 public function logs( $args ) {
1220 1400 if ( ( is_array( $args ) || $args instanceof Countable ? count( $args ) : 0 ) < 1 ) {
1221 1401 WP_CLI::error( 'Invalid command, add necessary arguments. See below...', false );
1222 - WP_CLI::runcommand( 'defender logs --help', [ 'launch' => false, 'exit_error' => false ] );
1402 + WP_CLI::runcommand(
1403 + 'defender logs --help',
1404 + array(
1405 + 'launch' => false,
1406 + 'exit_error' => false,
1407 + )
1408 + );
1223 1409
1224 1410 return;
1225 1411 }
1226 1412