PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.1
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
defender-security / src / traits / defender-bootstrap.php

defender-bootstrap.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.1, at src/traits/defender-bootstrap.php

725 lines 24.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handle common bootstrap functionalities.
4 *
5 * @package WP_Defender\Traits
6 */
7
8 namespace WP_Defender\Traits;
9
10 use WP_CLI;
11 use Calotes\DB\Mapper;
12 use WP_Defender\Admin;
13 use WP_Defender\Component\Cli;
14 use Calotes\Helper\Array_Cache;
15 use WP_Defender\Controller\HUB;
16 use WP_Defender\Controller\WAF;
17 use WP_Defender\Controller\Scan;
18 use WP_Defender\Component\Crypt;
19 use WP_Defender\Behavior\WPMUDEV;
20 use WP_Defender\Controller\Onboard;
21 use WP_Defender\Controller\Webauthn;
22 use WP_Defender\Controller\Dashboard;
23 use WP_Defender\Controller\Captcha;
24 use WP_Defender\Controller\Mask_Login;
25 use WP_Defender\Controller\Quarantine;
26 use WP_Defender\Controller\Two_Factor;
27 use WP_Defender\Component\Hub_Connector;
28 use WP_Defender\Controller\Main_Setting;
29 use WP_Defender\Controller\Notification;
30 use WP_Defender\Controller\Audit_Logging;
31 use WP_Defender\Controller\Data_Tracking;
32 use WP_Defender\Controller\Advanced_Tools;
33 use WP_Defender\Controller\Password_Reset;
34 use WP_Defender\Controller\Strong_Password;
35 use WP_Defender\Controller\Expert_Services;
36 use WP_Defender\Controller\Security_Tweaks;
37 use WP_Defender\Controller\Security_Headers;
38 use WP_Defender\Controller\Blocklist_Monitor;
39 use WP_Defender\Controller\Session_Protection;
40 use WP_Defender\Controller\Password_Protection;
41 use WP_Defender\Component\Network_Cron_Manager;
42 use WP_Defender\Component\Logger\Rotation_Logger;
43 use WP_Defender\Component\Firewall as Firewall_Component;
44 use WP_Defender\Controller\Firewall as Firewall_Controller;
45 use WP_Defender\Controller\Hub_Connector as Hub_Connector_Controller;
46 use WP_Defender\Model\Onboard as Onboard_Model;
47 use WP_Defender\Controller\Rate as Rate_Controller;
48 use WP_Defender\Component\Rate as Rate_Component;
49 use WP_Defender\Upgrader;
50
51 trait Defender_Bootstrap {
52 /**
53 * Table name for quarantine.
54 *
55 * @var string
56 */
57 private $quarantine_table = 'defender_quarantine';
58
59 /**
60 * Table name for scan item.
61 *
62 * @var string
63 */
64 private $scan_item_table = 'defender_scan_item';
65
66 /**
67 * Check is all quarantine dependent table is having storage engine InnoDB.
68 *
69 * @return bool True if all dependent table is InnoDB else false.
70 */
71 private function is_quarantine_dependent_tables_innodb(): bool {
72 global $wpdb;
73
74 $tables = array( $wpdb->users, $wpdb->base_prefix . $this->scan_item_table );
75 $total_table = count( $tables );
76
77 return $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
78 $wpdb->prepare(
79 "SELECT COUNT(`ENGINE`) = %d FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s AND `ENGINE` = %s AND TABLE_NAME IN ( '{$wpdb->users}', '{$wpdb->base_prefix}defender_scan_item' );",
80 $total_table,
81 $wpdb->dbname,
82 'innodb',
83 )
84 ) === '1';
85 }
86
87 /**
88 * Creates the quarantine table if it doesn't exist.
89 *
90 * @return void
91 */
92 public function create_table_quarantine() {
93 global $wpdb;
94
95 // Define table names and charset.
96 $quarantine_table = $wpdb->base_prefix . $this->quarantine_table;
97 $scan_item_table = $wpdb->base_prefix . $this->scan_item_table;
98 $charset_collate = $wpdb->get_charset_collate();
99 $unique_id = uniqid( $wpdb->prefix );
100
101 $common_columns = <<<'SQL'
102 `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
103 `defender_scan_item_id` int UNSIGNED DEFAULT NULL,
104 `file_hash` char(53) NOT NULL,
105 `file_full_path` text NOT NULL,
106 `file_original_name` tinytext NOT NULL,
107 `file_extension` varchar(16) DEFAULT NULL,
108 `file_mime_type` varchar(64) DEFAULT NULL,
109 `file_rw_permission` smallint UNSIGNED DEFAULT NULL,
110 `file_owner` varchar(255) DEFAULT NULL,
111 `file_group` varchar(255) DEFAULT NULL,
112 `file_version` varchar(32) DEFAULT NULL,
113 `file_category` tinyint UNSIGNED DEFAULT 0,
114 `file_modified_time` datetime NOT NULL,
115 `source_slug` varchar(255) NOT NULL,
116 `created_time` datetime NOT NULL,
117 `created_by` bigint UNSIGNED DEFAULT NULL,
118 PRIMARY KEY (`id`)
119 SQL;
120
121 // Define key names.
122 $scan_item_key = "{$unique_id}_defender_scan_item_id";
123 $created_by_key = "{$unique_id}_created_by";
124
125 // Build the SQL statement based on the storage engine.
126 if ( $this->is_quarantine_dependent_tables_innodb() ) {
127 $sql = <<<SQL
128 CREATE TABLE IF NOT EXISTS `{$quarantine_table}` (
129 $common_columns,
130 CONSTRAINT `{$scan_item_key}`
131 FOREIGN KEY (`defender_scan_item_id`) REFERENCES {$scan_item_table}(`id`)
132 ON UPDATE CASCADE ON DELETE SET NULL,
133 CONSTRAINT `{$created_by_key}`
134 FOREIGN KEY (`created_by`) REFERENCES {$wpdb->users}(`ID`)
135 ON UPDATE CASCADE ON DELETE SET NULL
136 ) {$charset_collate};
137 SQL;
138 } else {
139 $sql = <<<SQL
140 CREATE TABLE IF NOT EXISTS `{$quarantine_table}` (
141 $common_columns,
142 KEY `{$scan_item_key}` (`defender_scan_item_id`),
143 KEY `{$created_by_key}` (`created_by`)
144 ) {$charset_collate};
145 SQL;
146 }
147
148 // Execute the SQL query.
149 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
150 }
151
152 /**
153 * Activation.
154 */
155 private function activation_hook_common(): void {
156 Upgrader::date_activated();
157 $this->create_database_tables();
158 $this->on_activation();
159 // Create a file with a random key if it doesn't exist.
160 ( new Crypt() )->create_key_file();
161 // If this is a plugin reactivating, then track it. No need the check by 'wd_nofresh_install' key because the option is disabled by default.
162 $settings = wd_di()->get( Main_Setting::class );
163 $settings->set_intention( 'Reactivation' );
164 $settings->track_opt( true );
165
166 $service = wd_di()->get( Firewall_Component::class );
167 $service->auto_switch_ip_detection_option();
168 $service->maybe_show_misconfigured_ip_detection_option_notice();
169 $service->maybe_dismiss_cf_notice();
170 wp_schedule_single_event( time() + 5, 'wpdef_smart_ip_detection_ping' );
171 }
172
173 /**
174 * Deactivation.
175 */
176 public function deactivation_hook(): void {
177 wp_clear_scheduled_hook( 'firewall_clean_up_logs' );
178 wp_clear_scheduled_hook( 'audit_sync_events' );
179 wp_clear_scheduled_hook( 'audit_clean_up_logs' );
180 wp_clear_scheduled_hook( 'wdf_maybe_send_report' );
181 wp_clear_scheduled_hook( 'wp_defender_clear_logs' );
182 wp_clear_scheduled_hook( 'wpdef_sec_key_gen' );
183 wp_clear_scheduled_hook( 'wpdef_clear_scan_logs' );
184 wp_clear_scheduled_hook( 'wpdef_log_rotational_delete' );
185 wp_clear_scheduled_hook( 'wpdef_update_geoip' );
186 wp_clear_scheduled_hook( 'wpdef_fetch_global_ip_list' );
187 wp_clear_scheduled_hook( 'wpdef_quarantine_delete_expired' );
188 wp_clear_scheduled_hook( 'wpdef_firewall_clean_up_lockout' );
189 wp_clear_scheduled_hook( 'wpdef_firewall_send_compact_logs_to_api' );
190 wp_clear_scheduled_hook( 'wpdef_firewall_fetch_trusted_proxy_preset_ips' );
191 wp_clear_scheduled_hook( 'wpdef_firewall_clean_up_unlockout' );
192 wp_clear_scheduled_hook( 'wpdef_antibot_global_firewall_fetch_blocklist' );
193 wp_clear_scheduled_hook( 'wpdef_smart_ip_detection_ping' );
194 wp_clear_scheduled_hook( 'wpdef_confirm_antibot_toggle_on_hosting' );
195 wp_clear_scheduled_hook( 'wpdef_firewall_whitelist_server_public_ip' );
196 wp_clear_scheduled_hook( 'wpdef_rotate_malicious_bot_secret_hash' );
197
198 // Remove old legacy cron jobs if they exist.
199 wp_clear_scheduled_hook( 'lockoutReportCron' );
200 wp_clear_scheduled_hook( 'auditReportCron' );
201 wp_clear_scheduled_hook( 'cleanUpOldLog' );
202 wp_clear_scheduled_hook( 'scanReportCron' );
203 wp_clear_scheduled_hook( 'tweaksSendNotification' );
204 }
205
206 /**
207 * Creates the 'defender_unlockout' table if it doesn't exist in the database.
208 *
209 * @return void
210 */
211 public function create_table_unlockout() {
212 global $wpdb;
213
214 $charset_collate = $wpdb->get_charset_collate();
215
216 $sql = <<<SQL
217 CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_unlockout (
218 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
219 `ip` varchar(45) DEFAULT NULL,
220 `type` varchar(16) NOT NULL,
221 `email` varchar(255) NOT NULL,
222 `status` varchar(16) NOT NULL,
223 `timestamp` int(11) NOT NULL,
224 PRIMARY KEY (`id`),
225 KEY `ip` (`ip`),
226 KEY `type` (`type`),
227 KEY `email` (`email`),
228 KEY `status` (`status`)
229 ) {$charset_collate};
230 SQL;
231 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
232 }
233
234 /**
235 * Create blocklist table.
236 *
237 * @since 2.8.0
238 * @return void
239 */
240 public function create_table_blocklist(): void {
241 global $wpdb;
242
243 $charset_collate = $wpdb->get_charset_collate();
244
245 $sql = <<<SQL
246 CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_antibot (
247 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
248 `ip` varchar(45) NOT NULL,
249 `unlocked` tinyint(1) DEFAULT NULL,
250 `unlocked_at` int(11) DEFAULT NULL,
251 PRIMARY KEY (`id`),
252 UNIQUE KEY ip (ip)
253 ) {$charset_collate};
254 SQL;
255 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
256 }
257
258 /**
259 * Creates Defender's tables.
260 *
261 * @since 2.7.1 No use dbDelta because PHP v8.1 triggers an error when calling query "DESCRIBE {$table};" if the
262 * table doesn't exist.
263 */
264 protected function create_database_tables(): void {
265 global $wpdb;
266
267 $charset_collate = $wpdb->get_charset_collate();
268 // Hide errors.
269 $wpdb->hide_errors();
270 // Email log table.
271 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_email_log (
272 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
273 `timestamp` int NOT NULL,
274 `source` varchar(255) NOT NULL,
275 `to` varchar(255) NOT NULL,
276 PRIMARY KEY (`id`),
277 KEY `source` (`source`)
278 ) $charset_collate;";
279 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
280
281 // Audit log table. Though our data mainly store on API side, we will need a table for caching.
282 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_audit_log (
283 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
284 `timestamp` int NOT NULL,
285 `event_type` varchar(255) NOT NULL,
286 `action_type` varchar(255) NOT NULL,
287 `site_url` varchar(255) NOT NULL,
288 `user_id` int NOT NULL,
289 `context` varchar(255) NOT NULL,
290 `ip` varchar(45) NOT NULL,
291 `msg` varchar(255) NOT NULL,
292 `blog_id` int NOT NULL,
293 `synced` int NOT NULL,
294 `ttl` int NOT NULL,
295 PRIMARY KEY (`id`),
296 KEY `event_type` (`event_type`),
297 KEY `action_type` (`action_type`),
298 KEY `user_id` (`user_id`),
299 KEY `context` (`context`),
300 KEY `ip` (`ip`)
301 ) $charset_collate;";
302 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
303
304 // Scan item table.
305 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_scan_item (
306 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
307 `parent_id` int NOT NULL,
308 `type` varchar(255) NOT NULL,
309 `status` varchar(255) NOT NULL,
310 `raw_data` text NOT NULL,
311 PRIMARY KEY (`id`),
312 KEY `type` (`type`),
313 KEY `status` (`status`)
314 ) $charset_collate;";
315 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
316
317 // Scan table.
318 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_scan (
319 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
320 `percent` float NOT NULL,
321 `total_tasks` tinyint(4) NOT NULL,
322 `task_checkpoint` varchar(255) NOT NULL,
323 `status` varchar(255) NOT NULL,
324 `date_start` datetime NOT NULL,
325 `date_end` datetime NOT NULL,
326 `is_automation` bool NOT NULL,
327 PRIMARY KEY (`id`)
328 ) $charset_collate;";
329 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
330
331 // Lockout log table.
332 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_lockout_log (
333 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
334 `log` text,
335 `ip` varchar(45) DEFAULT NULL,
336 `date` int(11) DEFAULT NULL,
337 `type` varchar(16) DEFAULT NULL,
338 `user_agent` varchar(255) DEFAULT NULL,
339 `blog_id` int(11) DEFAULT NULL,
340 `tried` varchar(255),
341 `country_iso_code` char(2) DEFAULT NULL,
342 PRIMARY KEY (`id`),
343 KEY `ip` (`ip`),
344 KEY `type` (`type`),
345 KEY `tried` (`tried`),
346 KEY `country_iso_code` (`country_iso_code`)
347 ) $charset_collate;";
348 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
349
350 // Lockout table.
351 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->base_prefix}defender_lockout (
352 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
353 `ip` varchar(45) DEFAULT NULL,
354 `status` varchar(16) DEFAULT NULL,
355 `lockout_message` text,
356 `release_time` int(11) DEFAULT NULL,
357 `lock_time` int(11) DEFAULT NULL,
358 `lock_time_404` int(11) DEFAULT NULL,
359 `attempt` int(11) DEFAULT NULL,
360 `attempt_404` int(11) DEFAULT NULL,
361 `meta` text,
362 PRIMARY KEY (`id`),
363 KEY `ip` (`ip`),
364 KEY `status` (`status`),
365 KEY `attempt` (`attempt`),
366 KEY `attempt_404` (`attempt_404`)
367 ) $charset_collate;";
368 $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
369
370 $this->create_table_quarantine();
371
372 // Create Unlock table.
373 $this->create_table_unlockout();
374 // Create Blocklist table.
375 $this->create_table_blocklist();
376 }
377
378 /**
379 * Check if this is onboarding.
380 *
381 * @return bool
382 */
383 private function is_onboarding(): bool {
384 /**
385 * Display Onboarding if:
386 * it's a fresh install and there were no requests from the Hub before,
387 * after Reset Settings.
388 *
389 * @var HUB
390 */
391 $hub_class = wd_di()->get( HUB::class );
392 $hub_class->set_onboarding_status( Onboard_Model::maybe_show_onboarding() );
393
394 return $hub_class->get_onboarding_status() && ! defender_is_wp_cli();
395 }
396
397 /**
398 * Initialize the common modules of the application.
399 *
400 * @return void
401 */
402 private function init_modules_common(): void {
403 // Init main ORM.
404 Array_Cache::set( 'orm', new Mapper() );
405
406 if ( $this->is_onboarding() ) {
407 // If it's cli we should start this normally.
408 Array_Cache::set( 'onboard', wd_di()->get( Onboard::class ) );
409 } else {
410 // Initialize the main controllers of every module.
411 wd_di()->get( Dashboard::class );
412 }
413 wd_di()->get( Security_Tweaks::class );
414 wd_di()->get( Scan::class );
415 wd_di()->get( Audit_Logging::class );
416 wd_di()->get( Firewall_Controller::class );
417 wd_di()->get( WAF::class );
418 wd_di()->get( Two_Factor::class );
419 wd_di()->get( Advanced_Tools::class );
420 wd_di()->get( Mask_Login::class );
421 wd_di()->get( Security_Headers::class );
422 wd_di()->get( Captcha::class );
423 wd_di()->get( Notification::class );
424 wd_di()->get( Main_Setting::class );
425 wd_di()->get( Blocklist_Monitor::class );
426 wd_di()->get( Password_Protection::class );
427 wd_di()->get( Password_Reset::class );
428 wd_di()->get( Webauthn::class );
429 wd_di()->get( Expert_Services::class );
430 wd_di()->get( Hub_Connector_Controller::class );
431 wd_di()->get( Strong_Password::class );
432 wd_di()->get( Session_Protection::class );
433
434 if ( class_exists( 'WP_Defender\Controller\Quarantine' ) ) {
435 wd_di()->get( Quarantine::class );
436 }
437 wd_di()->get( Data_Tracking::class );
438 if ( defender_is_wp_org_version() ) {
439 wd_di()->get( Rate_Controller::class );
440 }
441
442 if ( is_multisite() ) {
443 wd_di()->get( Network_Cron_Manager::class );
444 }
445 }
446
447 /**
448 * Adds a specific class to the body tag if the current page is a Defender page.
449 *
450 * @param string $classes The existing body classes.
451 *
452 * @return string The modified body classes.
453 */
454 public function add_sui_to_body( $classes ) {
455 if ( ! is_defender_page() ) {
456 return $classes;
457 }
458 $classes .= sprintf( ' sui-%s ', DEFENDER_SUI );
459
460 return $classes;
461 }
462
463 /**
464 * Registers the necessary styles for the plugin.
465 *
466 * @return void
467 */
468 private function register_styles(): void {
469 wp_enqueue_style( 'defender-menu', WP_DEFENDER_BASE_URL . 'assets/css/defender-icon.css', array(), DEFENDER_VERSION );
470
471 $css_files = array(
472 'defender' => WP_DEFENDER_BASE_URL . 'assets/css/styles.css',
473 'def-sui' => WP_DEFENDER_BASE_URL . 'assets/css/shared-ui.css',
474 'def-admin' => WP_DEFENDER_BASE_URL . 'assets/css/admin.css',
475 );
476
477 foreach ( $css_files as $slug => $file ) {
478 wp_register_style( $slug, $file, array(), DEFENDER_VERSION );
479 }
480 }
481
482 /**
483 * Registers the necessary scripts for the plugin.
484 *
485 * @return void
486 */
487 private function register_scripts(): void {
488 $base_url = WP_DEFENDER_BASE_URL;
489 $dependencies = array( 'def-vue', 'def-manifest', 'defender', 'wp-i18n' );
490 $js_files = array(
491 'wpmudev-sui' => array(
492 $base_url . 'assets/js/shared-ui.js',
493 ),
494 'defender' => array(
495 $base_url . 'assets/js/scripts.js',
496 ),
497 'def-deactivation-survey' => array(
498 $base_url . 'assets/js/deactivation-survey.js',
499 array( 'clipboard', 'wpmudev-sui' ),
500 ),
501 'def-admin' => array(
502 $base_url . 'assets/js/admin.js',
503 array( 'def-deactivation-survey' ),
504 ),
505 'def-vue' => array(
506 $base_url . 'assets/js/vendor.js',
507 ),
508 'def-manifest' => array(
509 $base_url . 'assets/js/manifest.js',
510 ),
511 'def-dashboard' => array(
512 $base_url . 'assets/app/dashboard.js',
513 $dependencies,
514 ),
515 'def-securitytweaks' => array(
516 $base_url . 'assets/app/security-tweak.js',
517 array_merge( $dependencies, array( 'clipboard', 'wpmudev-sui' ) ),
518 ),
519 'def-scan' => array(
520 $base_url . 'assets/app/scan.js',
521 array_merge( $dependencies, array( 'clipboard', 'wpmudev-sui' ) ),
522 ),
523 'def-audit' => array(
524 $base_url . 'assets/app/audit.js',
525 $dependencies,
526 ),
527 'def-iplockout' => array(
528 $base_url . 'assets/app/ip-lockout.js',
529 array_merge( $dependencies, array( 'wpmudev-sui' ) ),
530 ),
531 'def-advancedtools' => array(
532 $base_url . 'assets/app/advanced-tools.js',
533 $dependencies,
534 ),
535 'def-settings' => array(
536 $base_url . 'assets/app/settings.js',
537 $dependencies,
538 ),
539 'def-2fa' => array(
540 $base_url . 'assets/app/two-fa.js',
541 $dependencies,
542 ),
543 'def-notification' => array(
544 $base_url . 'assets/app/notification.js',
545 $dependencies,
546 ),
547 'def-waf' => array(
548 $base_url . 'assets/app/waf.js',
549 $dependencies,
550 ),
551 'def-onboard' => array(
552 $base_url . 'assets/app/onboard.js',
553 $dependencies,
554 ),
555 'def-expert-services' => array(
556 $base_url . '/assets/app/expert-services.js',
557 $dependencies,
558 ),
559 );
560
561 foreach ( $js_files as $slug => $file ) {
562 if ( isset( $file[1] ) ) {
563 // This ensures that when JavaScript file changes,
564 // browsers will load the new version instead of
565 // serving a cached old version.
566 $file_path = str_replace( $base_url, WP_DEFENDER_DIR, $file[0] );
567 $file_version = file_exists( $file_path ) ? filemtime( $file_path ) : DEFENDER_VERSION;
568 wp_register_script( $slug, $file[0], $file[1], $file_version, true );
569 wp_set_script_translations( $slug, 'defender-security' );
570 } else {
571 wp_register_script( $slug, $file[0], array( 'jquery' ), DEFENDER_VERSION, true );
572 }
573 }
574 }
575
576 /**
577 * Localizes the script by adding necessary data to the 'defender' object.
578 *
579 * @return void
580 */
581 private function localize_script(): void {
582 $wpmu_dev = new WPMUDEV();
583 global $wp_defender_central;
584
585 $misc = array();
586 $data_tracking = wd_di()->get( Data_Tracking::class );
587 $is_tracking = $data_tracking->show_tracking_modal();
588 if ( $is_tracking ) {
589 $misc = $data_tracking->get_tracking_modal();
590 }
591 $misc['high_contrast'] = defender_high_contrast();
592 $is_wp_org = defender_is_wp_org_version();
593 if ( $is_wp_org ) {
594 $misc['rating'] = array();
595 $rate_service = Rate_Component::is_achievement_displayed();
596 if ( $rate_service['is_displayed'] ) {
597 $misc['rating'] = wd_di()->get( Rate_Controller::class )->data_frontend();
598 $misc['rating']['text'] = Rate_Component::get_notice_by_slug( $rate_service['slug'] );
599 }
600
601 $misc['rating']['is_displayed'] = $rate_service['is_displayed'];
602 $misc['rating']['type'] = $rate_service['slug'];
603 } else {
604 $misc['rating']['is_displayed'] = false;
605 }
606
607 wp_localize_script(
608 'def-vue',
609 'defender',
610 array(
611 'whitelabel' => defender_white_label_status(),
612 'misc' => $misc,
613 'home_url' => network_home_url(),
614 'site_url' => network_site_url(),
615 'admin_url' => network_admin_url(),
616 'defender_url' => WP_DEFENDER_BASE_URL,
617 // There might be Pro version but without pro features, for example when the membership expires.
618 'is_free' => $wpmu_dev->is_pro() ? 0 : 1,
619 // Strictly Free version.
620 'is_wp_org' => $is_wp_org ? 1 : 0,
621 'is_membership' => true,
622 'is_whitelabel' => $wpmu_dev->is_whitelabel_enabled() ? 'enabled' : 'disabled',
623 'wpmu_dev_url_action' => $wpmu_dev->hide_wpmu_dev_urls() ? 'hide' : 'show',
624 'opcache_save_comments' => $wp_defender_central->is_opcache_save_comments_disabled() ? 'disabled' : 'enabled',
625 'opcache_message' => $wp_defender_central->display_opcache_message(),
626 'wpmudev_url' => WP_DEFENDER_DOCS_LINK,
627 'wpmudev_support_ticket_text' => defender_support_ticket_text(),
628 'wpmudev_api_base_url' => $wpmu_dev->get_api_base_url(),
629 'upgrade_title' => esc_html__( 'UPGRADE TO PRO', 'defender-security' ),
630 'tracking_modal' => $is_tracking ? 'show' : 'hide',
631 'hosted' => $wpmu_dev->is_wpmu_hosting(),
632 'file_upload_nonce' => wp_create_nonce( 'defender_file_upload' ),
633 'wpmudev_hub_link' => 'https://wpmudev.com/hub2/',
634 )
635 );
636
637 wp_localize_script( 'defender', 'defenderGetText', defender_gettext_translations() );
638
639 wp_localize_script(
640 'def-deactivation-survey',
641 'defender',
642 array(
643 'usage_tracking' => wd_di()->get( \WP_Defender\Model\Setting\Main_Setting::class )->usage_tracking,
644 'admin_url' => admin_url( 'admin-ajax.php' ),
645 'nonce' => wp_create_nonce( 'defender_deactivation_survey_modal' ),
646 )
647 );
648 }
649
650 /**
651 * Register all core assets.
652 */
653 public function register_assets(): void {
654 $this->register_styles();
655 $this->register_scripts();
656 $this->localize_script();
657
658 do_action( 'defender_enqueue_assets' );
659 }
660
661 /**
662 * Trigger mandatory actions on activation.
663 */
664 private function on_activation(): void {
665 add_action(
666 'admin_init',
667 function () {
668 $security_tweaks = wd_di()->get( Security_Tweaks::class );
669 $security_tweaks->get_security_key()->cron_schedule();
670 }
671 );
672 }
673
674 /**
675 * Returns the cron schedules.
676 *
677 * @param array $schedules The existing cron schedules.
678 *
679 * @return array The updated cron schedules.
680 */
681 public function cron_schedules( array $schedules ) {
682 return defender_cron_schedules( $schedules );
683 }
684
685 /**
686 * Initialize the modules and register the plugin routes. Also include the admin class, adds WP-CLI commands.
687 *
688 * @return void
689 */
690 public function includes(): void {
691 // Initialize modules.
692 add_action(
693 'after_setup_theme',
694 function () {
695 add_filter( 'cron_schedules', array( $this, 'cron_schedules' ) );
696 $this->init_modules();
697 }
698 );
699 // Register routes.
700 add_action(
701 'init',
702 function () {
703 require_once WP_DEFENDER_DIR . 'src/routes.php';
704 },
705 9
706 );
707 // Register the Hub Connector early to handle the auth callback during the admin init hook.
708 add_action( 'plugins_loaded', array( wd_di()->get( Hub_Connector::class ), 'init' ) );
709 // Register the Cross-Sell module.
710 add_action( 'init', array( wd_di()->get( \WP_Defender\Component\Cross_Sell::class ), 'init' ), 9 );
711 // Include admin class. Don't use is_admin().
712 add_action( 'admin_init', array( ( new Admin() ), 'init' ) );
713 // Initialize deactivation survey.
714 add_action( 'admin_enqueue_scripts', array( ( new Admin() ), 'init_deactivation_survey' ) );
715 // Add WP-CLI commands.
716 if ( defender_is_wp_cli() ) {
717 WP_CLI::add_command( 'defender', Cli::class );
718 }
719 // Rotational logger initialization.
720 add_action( 'init', array( ( new Rotation_Logger() ), 'init' ), 99 );
721 // Handle plugin deactivation.
722 add_action( 'deactivated_plugin', array( ( new HUB() ), 'intercept_deactivate' ) );
723 }
724 }
725