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

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

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