PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.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 / component / class-backup-settings.php

class-backup-settings.php in Defender Security – Malware Scanner, Login Security & Firewall 5.3.1, at src/component/class-backup-settings.php

1,523 lines 57.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles backup and restoration of settings.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use Countable;
11 use WP_Defender\Component;
12 use WP_Defender\Traits\IP;
13 use WP_Defender\Behavior\WPMUDEV;
14 use WP_Defender\Controller\Blacklist;
15 use WP_Defender\Controller\Global_Ip;
16 use WP_Defender\Controller\Two_Factor;
17 use WP_Defender\Controller\Nf_Lockout;
18 use WP_Defender\Controller\Main_Setting;
19 use WP_Defender\Controller\Audit_Logging;
20 use WP_Defender\Controller\Security_Headers;
21 use WP_Defender\Controller\Blocklist_Monitor;
22 use WP_Defender\Model\Notification\Audit_Report;
23 use WP_Defender\Model\Setting\Global_Ip_Lockout;
24 use WP_Defender\Model\Setting\Scan as Model_Scan;
25 use WP_Defender\Controller\Scan as Controller_Scan;
26 use WP_Defender\Model\Notification\Malware_Report;
27 use WP_Defender\Model\Notification\Tweak_Reminder;
28 use WP_Defender\Model\Notification\Firewall_Report;
29 use WP_Defender\Component\Config\Config_Hub_Helper;
30 use WP_Defender\Model\Setting\Two_Fa as Model_Two_Fa;
31 use WP_Defender\Component\Security_Tweaks\Security_Key;
32 use WP_Defender\Model\Setting\Antibot_Global_Firewall_Setting;
33 use WP_Defender\Model\Notification\Malware_Notification;
34 use WP_Defender\Model\Setting\Firewall as Model_Firewall;
35 use WP_Defender\Model\Notification\Firewall_Notification;
36 use WP_Defender\Controller\Firewall as Controller_Firewall;
37 use WP_Defender\Model\Setting\Mask_Login as Model_Mask_Login;
38 use WP_Defender\Component\Security_Tweaks\Prevent_Enum_Users;
39 use WP_Defender\Controller\Mask_Login as Controller_Mask_Login;
40 use WP_Defender\Controller\UA_Lockout as Controller_Ua_Lockout;
41 use WP_Defender\Model\Setting\Main_Setting as Model_Main_Setting;
42 use WP_Defender\Model\Setting\Audit_Logging as Model_Audit_Logging;
43 use WP_Defender\Model\Setting\Login_Lockout as Model_Login_Lockout;
44 use WP_Defender\Model\Setting\User_Agent_Lockout as Model_Ua_Lockout;
45 use WP_Defender\Model\Setting\Security_Tweaks as Model_Security_Tweaks;
46 use WP_Defender\Controller\Security_Tweaks as Controller_Security_Tweaks;
47 use WP_Defender\Model\Setting\Notfound_Lockout as Model_Notfound_Lockout;
48 use WP_Defender\Model\Setting\Security_Headers as Model_Security_Headers;
49 use WP_Defender\Model\Setting\Blacklist_Lockout as Model_Blacklist_Lockout;
50 use WP_Defender\Model\Setting\Password_Protection as Model_Password_Protection;
51 use WP_Defender\Controller\Password_Protection as Controller_Password_Protection;
52
53 /**
54 * Handles backup and restoration of settings.
55 */
56 class Backup_Settings extends Component {
57
58 use IP;
59
60 public const KEY = 'defender_last_settings', INDEXER = 'defender_config_indexer';
61
62 /**
63 * Indicates whether the current installation is a pro version.
64 *
65 * @var bool
66 */
67 private $is_pro;
68
69 /**
70 * Constructor for the Backup_Settings class.
71 * It initializes the class and sets whether the current installation is a pro version.
72 */
73 public function __construct() {
74 $this->is_pro = ( new WPMUDEV() )->is_pro();
75 }
76
77 /**
78 * Changes the format of subscribers in a notification object to an array format.
79 *
80 * @param object $notification_object The notification object to format.
81 *
82 * @return array Returns an array of subscribers formatted.
83 */
84 public function change_subscriber_format( $notification_object ): array {
85 if (
86 ! is_object( $notification_object )
87 && ! is_array( $notification_object->in_house_recipients )
88 && ! is_array( $notification_object->out_house_recipients )
89 ) {
90 return array();
91 }
92 if ( empty( $notification_object->in_house_recipients ) && empty( $notification_object->out_house_recipients ) ) {
93 return array();
94 }
95
96 $subscribers = array();
97 if ( ! empty( $notification_object->in_house_recipients ) ) {
98 $subscribers['in_house_recipients'] = $notification_object->in_house_recipients;
99 }
100 if ( ! empty( $notification_object->out_house_recipients ) ) {
101 $subscribers['out_house_recipients'] = $notification_object->out_house_recipients;
102 }
103
104 return $subscribers;
105 }
106
107 /**
108 * Gather settings from all modules.
109 *
110 * @return array
111 */
112 public function gather_data(): array {
113 $audit = array();
114 $tweak_class = wd_di()->get( Controller_Security_Tweaks::class );
115 $tweak_class->refresh_tweaks_status();
116 $settings = new Model_Security_Tweaks();
117 $tweak_notification = new Tweak_Reminder();
118 $security_tweaks = array(
119 'notification_repeat' => $tweak_notification->configs['reminder'],
120 'subscribers' => $this->change_subscriber_format( $tweak_notification ),
121 'notification' => $tweak_notification->status,
122 'data' => $settings->data,
123 'fixed' => $settings->fixed,
124 'issues' => $settings->issues,
125 'ignore' => $settings->ignore,
126 'automate' => $settings->automate,
127 'enabled_user_enums' => $this->get_enabled_user_enums(),
128 'security_key' => $this->get_security_key_all_options(),
129 );
130 $settings = new Model_Scan();
131 $scan_report = new Malware_Report();
132 $scan_notification = new Malware_Notification();
133 $scan = array(
134 'integrity_check' => $settings->integrity_check,
135 'check_core' => $settings->check_core,
136 'check_plugins' => $settings->check_plugins,
137 'check_known_vuln' => $settings->check_known_vuln,
138 'scan_malware' => $settings->scan_malware,
139 'filesize' => $settings->filesize,
140 // @since 2.7.0 changes for Scheduled options.
141 'scheduled_scanning' => $settings->scheduled_scanning,
142 'day' => $settings->day,
143 'day_n' => $settings->day_n,
144 'time' => $settings->time,
145 'frequency' => $settings->frequency,
146 'report' => $scan_report->status,
147 'always_send' => $scan_report->configs['always_send'],
148 'report_subscribers' => $this->change_subscriber_format( $scan_report ),
149 // @since 2.7.0 for backward compatibility. We can remove it in the next version.
150 'dry_run' => false,
151 'notification' => $scan_notification->status,
152 'always_send_notification' => $scan_notification->configs['always_send'],
153 'error_send' => $scan_notification->configs['error_send'],
154 'notification_subscribers' => $this->change_subscriber_format( $scan_notification ),
155 'email_subject_issue_not_found' => $scan_notification->configs['template']['not_found']['subject'],
156 'email_subject_issue_found' => $scan_notification->configs['template']['found']['subject'],
157 'email_subject_error' => $scan_notification->configs['template']['error']['subject'],
158 'email_content_issue_not_found' => $scan_notification->configs['template']['not_found']['body'],
159 'email_content_issue_found' => $scan_notification->configs['template']['found']['body'],
160 'email_content_error' => $scan_notification->configs['template']['error']['body'],
161 );
162 if ( class_exists( Model_Audit_Logging::class ) ) {
163 $settings = new Model_Audit_Logging();
164 $audit_report = new Audit_Report();
165 $audit = array(
166 'enabled' => $settings->is_active(),
167 'report' => $audit_report->status,
168 'subscribers' => $this->change_subscriber_format( $audit_report ),
169 'frequency' => $audit_report->frequency,
170 'day' => $audit_report->day,
171 'day_n' => $audit_report->day_n,
172 'time' => $audit_report->time,
173 // @since 2.7.0 We can remove it in the next version.
174 'dry_run' => false,
175 'storage_days' => $settings->storage_days,
176 );
177 if ( ! $this->is_pro ) {
178 $audit['enabled'] = false;
179 }
180 } else {
181 $audit['enabled'] = false;
182 }
183 $settings_firewall = new Model_Firewall();
184 $settings_ll = new Model_Login_Lockout();
185 $settings_nl = new Model_Notfound_Lockout();
186 $settings_bl = new Model_Blacklist_Lockout();
187 $lockout_notification = new Firewall_Notification();
188 $lockout_report = new Firewall_Report();
189 $ua_banning_model = new Model_Ua_Lockout();
190 $settings_gi = wd_di()->get( Global_Ip_Lockout::class );
191 $settings_antibot = wd_di()->get( Antibot_Global_Firewall_Setting::class );
192 $iplockout = array(
193 'login_protection' => $settings_ll->enabled,
194 'login_protection_login_attempt' => $settings_ll->attempt,
195 'login_protection_lockout_timeframe' => $settings_ll->timeframe,
196 'login_protection_lockout_ban' => $settings_ll->lockout_type,
197 'login_protection_lockout_duration' => $settings_ll->duration,
198 'login_protection_lockout_duration_unit' => $settings_ll->duration_unit,
199 'login_protection_lockout_message' => $settings_ll->lockout_message,
200 'username_blacklist' => $settings_ll->username_blacklist,
201 'detect_404' => $settings_nl->enabled,
202 'detect_404_threshold' => $settings_nl->attempt,
203 'detect_404_timeframe' => $settings_nl->timeframe,
204 'detect_404_lockout_ban' => $settings_nl->lockout_type,
205 'detect_404_lockout_duration' => $settings_nl->duration,
206 'detect_404_lockout_duration_unit' => $settings_nl->duration_unit,
207 'detect_404_lockout_message' => $settings_nl->lockout_message,
208 'detect_404_blacklist' => $settings_nl->blacklist,
209 'detect_404_whitelist' => $settings_nl->whitelist,
210 'detect_404_logged' => $settings_nl->detect_logged,
211 'ip_blacklist' => $settings_bl->ip_blacklist,
212 'ip_whitelist' => $settings_bl->ip_whitelist,
213 'country_blacklist' => $settings_bl->country_blacklist,
214 'country_whitelist' => $settings_bl->country_whitelist,
215 'ip_lockout_message' => $settings_bl->ip_lockout_message,
216 'login_lockout_notification' => $lockout_notification->configs['login_lockout'],
217 'ip_lockout_notification' => $lockout_notification->configs['nf_lockout'],
218 'notification' => $lockout_notification->status,
219 'notification_subscribers' => $this->change_subscriber_format( $lockout_notification ),
220 'cooldown_enabled' => $lockout_notification->configs['limit'],
221 'cooldown_number_lockout' => $lockout_notification->configs['threshold'],
222 'cooldown_period' => $lockout_notification->configs['cool_off'],
223 'report' => $lockout_report->status,
224 'report_subscribers' => $this->change_subscriber_format( $lockout_report ),
225 'report_frequency' => $lockout_report->frequency,
226 'day' => $lockout_report->day,
227 'day_n' => $lockout_report->day_n,
228 'report_time' => $lockout_report->time,
229 // @since 2.7.0 We can remove it in the next version.
230 'dry_run' => false,
231 'storage_days' => $settings_firewall->storage_days,
232 'geoIP_db' => $settings_bl->geodb_path,
233 'maxmind_license_key' => $settings_bl->maxmind_license_key,
234 'ip_blocklist_cleanup_interval' => $settings_firewall->ip_blocklist_cleanup_interval,
235 'ua_banning_enabled' => $ua_banning_model->enabled,
236 'ua_banning_message' => $ua_banning_model->message,
237 'ua_banning_blacklist' => $ua_banning_model->blacklist,
238 'ua_banning_whitelist' => $ua_banning_model->whitelist,
239 'ua_banning_empty_headers' => $ua_banning_model->empty_headers,
240 'global_ip_list' => $settings_gi->enabled,
241 'global_ip_list_blocklist_autosync' => $settings_gi->blocklist_autosync,
242 'antibot' => $settings_antibot->enabled,
243 );
244 $settings_two_fa = new Model_Two_Fa();
245 $settings_mask_login = new Model_Mask_Login();
246 $advanced_tools = array(
247 'two_factor' => $settings_two_fa->export(),
248 'mask_login' => $settings_mask_login->export(),
249 );
250 $settings_main = new Model_Main_Setting();
251 $main_settings = $settings_main->export();
252 $settings_sec_headers = new Model_Security_Headers();
253 $security_headers = $settings_sec_headers->export();
254 $ret = array(
255 'scan' => $scan,
256 'iplockout' => $iplockout,
257 'two_factor' => $advanced_tools['two_factor'],
258 'mask_login' => $advanced_tools['mask_login'],
259 'settings' => $main_settings,
260 'security_headers' => $security_headers,
261 );
262 if ( isset( $audit ) ) {
263 $ret['audit'] = $audit;
264 }
265 // For Blocklist_Monitor.
266 if ( $this->is_pro ) {
267 $blocklist_monitor_class = wd_di()->get( Blocklist_Monitor::class );
268 $status = (string) $blocklist_monitor_class->get_status();
269 $ret['blocklist_monitor']['enabled'] = '1' === $status;
270 $ret['blocklist_monitor']['status'] = $status;
271 } else {
272 $ret['blocklist_monitor']['enabled'] = false;
273 }
274 // For Pwned passwords.
275 $pwned_password_model = wd_di()->get( Model_Password_Protection::class );
276 $ret['pwned_passwords']['enabled'] = $pwned_password_model->is_active();
277 $ret['pwned_passwords']['user_roles'] = $pwned_password_model->user_roles;
278 $ret['pwned_passwords']['custom_message'] = $pwned_password_model->pwned_actions['force_change_message'];
279 // It's better to add Tweaks as the last key to eliminate unexpected behavior with possible user logout.
280 $ret['security_tweaks'] = $security_tweaks;
281
282 return $ret;
283 }
284
285 /**
286 * Retrieves all configurations from the options table.
287 *
288 * @return array Returns an array of all configurations.
289 */
290 public function get_configs(): array {
291 $keys = get_site_option( self::INDEXER, false );
292 $results = array();
293 if ( empty( $keys ) ) {
294 return $results;
295 }
296
297 foreach ( $keys as $key ) {
298 $config = get_site_option( $key );
299
300 if ( false === $config ) {
301 $this->remove_index( $key );
302 } else {
303 $results[ $key ] = $config;
304 }
305 }
306
307 return $results;
308 }
309
310 /**
311 * Sets a specific configuration as active.
312 *
313 * @param string $key The key of the configuration to make active.
314 */
315 public function make_config_active( string $key ): void {
316 $configs = $this->get_configs();
317 foreach ( $configs as $k => $config ) {
318 if ( $k === $key ) {
319 $config['is_active'] = true;
320 } else {
321 $config['is_active'] = false;
322 }
323 update_site_option( $k, $config );
324 }
325 }
326
327 /**
328 * Clear keys if the last config was deleted.
329 *
330 * @return void
331 */
332 public function clear_keys(): void {
333 $keys = get_site_option( self::INDEXER, array() );
334 if ( empty( $keys ) ) {
335 delete_site_option( self::INDEXER );
336 delete_site_option( self::KEY );
337 }
338 }
339
340 /**
341 * Clears all configurations from the options table.
342 */
343 public function clear_configs(): void {
344 $keys = get_site_option( self::INDEXER, false );
345 if ( is_array( $keys ) && ! empty( $keys ) ) {
346 foreach ( $keys as $key ) {
347 delete_site_option( $key );
348 }
349 }
350 delete_site_option( self::INDEXER );
351 delete_site_option( self::KEY );
352 delete_site_transient( Config_Hub_Helper::CONFIGS_TRANSIENT_KEY );
353 }
354
355 /**
356 * Do the deletion only if there are no indexed configs, so as not to delete the displayed configs.
357 */
358 protected function remove_unindexed_configs() {
359 global $wpdb;
360
361 $result = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
362 $wpdb->prepare(
363 "SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE %s",
364 '%wp_defender_config_%'
365 ),
366 ARRAY_A
367 );
368 if ( ! empty( $result ) ) {
369 foreach ( $result as $arr ) {
370 delete_site_option( $arr['option_name'] );
371 }
372 }
373 }
374
375 /**
376 * Create a default config.
377 *
378 * @return void
379 */
380 public function maybe_create_default_config(): void {
381 $keys = get_site_option( self::INDEXER, array() );
382 if ( empty( $keys ) ) {
383 $this->remove_unindexed_configs();
384 $key = 'wp_defender_config_default' . time();
385 if ( ! get_site_option( $key ) ) {
386 $this->create_basic_config( $key );
387 }
388 }
389 }
390
391 /**
392 * Creates a basic configuration and saves it to the options table.
393 *
394 * @param string $key The key under which to save the configuration.
395 *
396 * @return void
397 * @since 2.6.5 Reduce differences between Basic and Default security configs.
398 */
399 private function create_basic_config( string $key ): void {
400 $configs = array();
401 // @since 2.6.5 Clear recipients.
402 $default_recipients = array();
403 // Init Tweaks class and refresh status.
404 $tweak_class = wd_di()->get( Controller_Security_Tweaks::class );
405 $tweak_class->refresh_tweaks_status();
406 // Get user role keys.
407 $user_roles = array_keys( get_editable_roles() );
408 // Default values.
409 $default_scan_notification_values = ( new Malware_Notification() )->get_default_values();
410 $default_login_lockout_values = ( new Model_Login_Lockout() )->get_default_values();
411 $default_404_lockout_values = ( new Model_Notfound_Lockout() )->get_default_values();
412 $default_ip_lockout_values = ( new Model_Blacklist_Lockout() )->get_default_values();
413 $default_ua_lockout_values = ( new Model_Ua_Lockout() )->get_default_values();
414 $default_password_protection_values = ( new Model_Password_Protection() )->get_default_values();
415 $default_2fa_values = ( new Model_Two_Fa() )->get_default_values();
416 // Total data.
417 $data = array(
418 'scan' => array(
419 'integrity_check' => true,
420 'check_core' => true,
421 'check_plugins' => true,
422 'check_known_vuln' => true,
423 'scan_malware' => true,
424 'filesize' => 3,
425 'report' => 'enabled',
426 'always_send' => false,
427 'report_subscribers' => $default_recipients,
428 'day' => 'sunday',
429 'day_n' => '1',
430 'time' => '4:00',
431 'frequency' => 'weekly',
432 // @since 2.7.0 We can remove it in the next version.
433 'dry_run' => false,
434 'notification' => 'enabled',
435 'always_send_notification' => false,
436 'error_send' => false,
437 'notification_subscribers' => $default_recipients,
438 'email_subject_issue_found' => $default_scan_notification_values['subject_issue_found'],
439 'email_subject_issue_not_found' => $default_scan_notification_values['subject_issue_not_found'],
440 'email_subject_error' => $default_scan_notification_values['subject_error'],
441 'email_content_issue_found' => $default_scan_notification_values['content_issue_found'],
442 'email_content_issue_not_found' => $default_scan_notification_values['content_issue_not_found'],
443 'email_content_error' => $default_scan_notification_values['content_error'],
444 // @since 2.7.0 move Scheduled options from Malware Scanning - Reporting to Malware settings.
445 // Values for frequency, day and time are above.
446 'scheduled_scanning' => true,
447 ),
448 'iplockout' => array(
449 'login_protection' => true,
450 'login_protection_login_attempt' => '5',
451 'login_protection_lockout_timeframe' => '300',
452 'login_protection_lockout_ban' => 'timeframe',
453 'login_protection_lockout_duration' => '4',
454 'login_protection_lockout_duration_unit' => 'hours',
455 'login_protection_lockout_message' => $default_login_lockout_values['message'],
456 // @since 2.7.0 New default blocklisted usernames.
457 'username_blacklist' => "adm\nadmin\nadmin1\nhostname\nmanager\nqwerty\nroot\nsupport\nsysadmin\ntest\nuser\nadministrator",
458 'detect_404' => true,
459 'detect_404_threshold' => '20',
460 'detect_404_timeframe' => '300',
461 'detect_404_lockout_ban' => 'timeframe',
462 'detect_404_lockout_duration' => '4',
463 'detect_404_lockout_duration_unit' => 'hours',
464 'detect_404_lockout_message' => $default_404_lockout_values['message'],
465 'detect_404_blacklist' => '',
466 // @since 2.7.0 New default whitelisted extensions.
467 'detect_404_whitelist' => ".css\n.js\n.jpg\n.png\n.gif\n.map",
468 'detect_404_logged' => true,
469 'ip_blacklist' => '',
470 // @since 2.6.5 Clear the current user IP.
471 'ip_whitelist' => '',
472 'country_blacklist' => '',
473 'country_whitelist' => '',
474 'ip_lockout_message' => $default_ip_lockout_values['message'],
475 'login_lockout_notification' => true,
476 'ip_lockout_notification' => true,
477 'notification' => 'enabled',
478 'notification_subscribers' => $default_recipients,
479 'cooldown_enabled' => false,
480 'cooldown_number_lockout' => '3',
481 'cooldown_period' => '24',
482 'report' => 'enabled',
483 'report_subscribers' => $default_recipients,
484 'report_frequency' => 'weekly',
485 'day' => 'sunday',
486 'day_n' => '1',
487 'report_time' => '4:00',
488 // @since 2.7.0 We can remove it in the next version.
489 'dry_run' => false,
490 'storage_days' => '180',
491 'geoIP_db' => '',
492 'ip_blocklist_cleanup_interval' => 'never',
493 'ua_banning_enabled' => false,
494 'ua_banning_message' => $default_ua_lockout_values['message'],
495 'ua_banning_blacklist' => $default_ua_lockout_values['blacklist'],
496 'ua_banning_whitelist' => $default_ua_lockout_values['whitelist'],
497 'ua_banning_empty_headers' => false,
498 // @since 2.7.1 New key of Blacklist_Lockout.
499 'maxmind_license_key' => '',
500 // @since 3.4.0 New Global IP list.
501 'global_ip_list' => false,
502 'global_ip_list_blocklist_autosync' => false,
503 ),
504 'two_factor' => array(
505 // @since 2.6.5 Disabled module.
506 'enabled' => false,
507 'lost_phone' => true,
508 'force_auth' => false,
509 'force_auth_mess' => $default_2fa_values['message'],
510 'user_roles' => $user_roles,
511 'force_auth_roles' => array(),
512 'custom_graphic' => false,
513 'custom_graphic_type' => Model_Two_Fa::CUSTOM_GRAPHIC_TYPE_UPLOAD,
514 'custom_graphic_url' => '',
515 'custom_graphic_link' => '',
516 'email_subject' => $default_2fa_values['email_subject'],
517 'email_sender' => $default_2fa_values['email_sender'],
518 'email_body' => $default_2fa_values['email_body'],
519 'app_title' => $default_2fa_values['app_title'],
520 ),
521 'mask_login' => array(
522 'enabled' => false,
523 'mask_url' => '',
524 'redirect_traffic' => 'off',
525 'redirect_traffic_url' => '',
526 'redirect_traffic_page_id' => 0,
527 ),
528 'security_headers' => array(
529 'sh_xframe' => true,
530 'sh_xframe_mode' => 'sameorigin',
531 'sh_xframe_urls' => '',
532 'sh_xss_protection' => true,
533 'sh_xss_protection_mode' => 'sanitize',
534 'sh_content_type_options' => true,
535 'sh_content_type_options_mode' => 'nosniff',
536 'sh_strict_transport' => true,
537 'hsts_preload' => false,
538 'include_subdomain' => false,
539 'hsts_cache_duration' => '30 days',
540 'sh_referrer_policy' => true,
541 'sh_referrer_policy_mode' => 'origin-when-cross-origin',
542 'sh_feature_policy' => true,
543 'sh_feature_policy_mode' => 'self',
544 'sh_feature_policy_urls' => '',
545 ),
546 'settings' => array(
547 'uninstall_data' => 'keep',
548 'uninstall_settings' => 'preserve',
549 // @since 2.7.0 Empty.
550 'translate' => '',
551 'usage_tracking' => false,
552 'high_contrast_mode' => false,
553 ),
554 'pwned_passwords' => array(
555 'enabled' => false,
556 'user_roles' => $user_roles,
557 'custom_message' => $default_password_protection_values['message'],
558 ),
559 );
560 // Pro properties.
561 if ( $this->is_pro ) {
562 $data['audit'] = array(
563 'enabled' => true,
564 'report' => 'enabled',
565 'subscribers' => $default_recipients,
566 'frequency' => 'weekly',
567 'day' => 'sunday',
568 'day_n' => '1',
569 'time' => '4:00',
570 // @since 2.7.0 We can remove it in the next version.
571 'dry_run' => false,
572 'storage_days' => '6 months',
573 );
574 $data['blocklist_monitor'] = array(
575 // @since 2.7.0 Enable.
576 'enabled' => true,
577 'status' => '1',
578 );
579 } else {
580 $data['audit']['enabled'] = false;
581 $data['blocklist_monitor']['enabled'] = false;
582 }
583 // It's better to add Tweaks as the last key to eliminate unexpected behavior with possible user logout.
584 $data['security_tweaks'] = array(
585 'notification_repeat' => 'weekly',
586 'subscribers' => $default_recipients,
587 'notification' => 'enabled',
588 'automate' => true,
589 'data' => array(),
590 // @since 2.7.0 Specific values for 4 fixed, 0 ignored and 8 actioned tweaks. 12 tweaks in total.
591 'fixed' => array(
592 'disable-xml-rpc',
593 'login-duration',
594 'disable-trackback',
595 'prevent-enum-users',
596 ),
597 'issues' => array(
598 'php-version',
599 'wp-version',
600 'prevent-php-executed',
601 'protect-information',
602 'replace-admin-username',
603 'security-key',
604 'disable-file-editor',
605 'hide-error',
606 ),
607 'ignore' => array(),
608 );
609
610 $configs['configs'] = $data;
611 $configs['strings'] = $this->create_default_module_strings( $data, $this->is_pro );
612 $configs['name'] = esc_html__( 'Basic Config', 'defender-security' );
613 $configs['description'] = esc_html__( 'Recommended default protection for every site', 'defender-security' );
614 $configs['immortal'] = true;
615 $configs['is_removable'] = true;
616 update_site_option( $key, $configs );
617 $this->index_key( $key );
618 }
619
620 /**
621 * Adds a configuration key to the indexer in the options table.
622 *
623 * @param string $key The configuration key to index.
624 */
625 public function index_key( string $key ): void {
626 $keys = get_site_option( self::INDEXER, array() );
627 // Check for uniqueness.
628 if ( is_array( $keys ) ) {
629 $keys[ $key ] = $key;
630 $keys = array_unique( $keys );
631 update_site_option( self::INDEXER, $keys );
632 } elseif ( empty( $keys ) ) {
633 // The first config.
634 update_site_option( self::INDEXER, array( $key => $key ) );
635 }
636 }
637
638 /**
639 * Removes a configuration key from the indexer in the options table.
640 *
641 * @param string $key The configuration key to remove from the index.
642 */
643 public function remove_index( string $key ): void {
644 $keys = get_site_option( self::INDEXER, false );
645 unset( $keys[ array_search( $key, $keys, true ) ] );
646 update_site_option( self::INDEXER, $keys );
647 }
648
649 /**
650 * Backup the previous data before we process new version.
651 *
652 * @return array
653 */
654 public function backup_data(): array {
655 $data = $this->get_prev_settings();
656 $old_backup = get_site_option( self::KEY );
657 if ( ! is_array( $old_backup ) ) {
658 $old_backup = array();
659 }
660 if ( count( $old_backup ) > 20 ) {
661 // Remove the oldest key.
662 $old_backup = array_shift( $old_backup );
663 }
664 $version = get_site_option( 'wd_db_version' );
665 $old_backup[ $version . '_' . time() ] = $data;
666 update_site_option( self::KEY, $old_backup );
667
668 return $data;
669 }
670
671 /**
672 * Restores data from a backup.
673 *
674 * @param array $data The data to restore.
675 * @param string $request_reason The reason for the data restoration.
676 *
677 * @return bool|string Returns true if re-authentication is needed, otherwise false.
678 */
679 public function restore_data( array $data, string $request_reason = 'plugin' ) {
680 $need_reauth = false;
681 foreach ( $data as $module => $module_data ) {
682 if ( ! is_array( $module_data ) ) {
683 continue;
684 }
685
686 $controller = $this->module_to_controller( $module, true );
687 // Return array of objects if the module is IP Lockout.
688 if ( is_object( $controller ) || is_array( $controller ) ) {
689 foreach ( $module_data as &$value ) {
690 if ( ! is_array( $value ) && ! filter_var( $value, FILTER_VALIDATE_BOOLEAN ) ) {
691 $value = str_replace( '{nl}', PHP_EOL, $value );
692 }
693 }
694
695 /**
696 * Import the data and perform separate actions for each of the main modules:
697 * Scan, Firewall, Audit, Block list Monitor, Pwned Passwords,
698 * 2FA, Mask Login, Security Headers, Main settings, Tweaks.
699 */
700 if ( 'scan' === $module ) {
701 // Import.
702 $controller->import_data( $module_data );
703
704 $scan_notification = new Malware_Notification();
705 $scan_report = new Malware_Report();
706 if ( ! empty( $module_data ) ) {
707 $scan_settings = new Model_Scan();
708 // For Scan notification.
709 if ( isset( $module_data['notification'] ) ) {
710 if ( $scan_notification->status !== $module_data['notification'] ) {
711 $scan_notification->status = $module_data['notification'];
712 }
713 if ( isset( $module_data['always_send_notification'] ) ) {
714 $scan_notification->configs['always_send'] = $module_data['always_send_notification'];
715 }
716 if ( isset( $module_data['error_send'] ) ) {
717 $scan_notification->configs['error_send'] = $module_data['error_send'];
718 }
719 if ( ! empty( $module_data['email_subject_issue_found'] ) ) {
720 $scan_notification->configs['template']['found']['subject'] = $module_data['email_subject_issue_found'];
721 }
722 if ( ! empty( $module_data['email_subject_issue_not_found'] ) ) {
723 $scan_notification->configs['template']['not_found']['subject'] = $module_data['email_subject_issue_not_found'];
724 }
725 if ( ! empty( $module_data['email_subject_error'] ) ) {
726 $scan_notification->configs['template']['error']['subject'] = $module_data['email_subject_error'];
727 }
728 if ( ! empty( $module_data['email_content_issue_found'] ) ) {
729 $scan_notification->configs['template']['found']['body'] = $module_data['email_content_issue_found'];
730 }
731 if ( ! empty( $module_data['email_content_issue_not_found'] ) ) {
732 $scan_notification->configs['template']['not_found']['body'] = $module_data['email_content_issue_not_found'];
733 }
734 if ( ! empty( $module_data['email_content_error'] ) ) {
735 $scan_notification->configs['template']['error']['body'] = $module_data['email_content_error'];
736 }
737 if ( ! empty( $module_data['notification_subscribers'] ) ) {
738 // Reset all recipients before.
739 $scan_notification->in_house_recipients = array();
740 $scan_notification->out_house_recipients = array();
741 foreach ( $module_data['notification_subscribers'] as $key => $subscribers ) {
742 $scan_notification->$key = $subscribers;
743 }
744 }
745 $scan_notification->save();
746 }
747 // For Scan report.
748 if (
749 $this->is_pro
750 && isset( $module_data['report'] )
751 && $scan_report->status !== $module_data['report']
752 ) {
753 $scan_report->status = $module_data['report'];
754 }
755 if ( isset( $module_data['always_send'] ) ) {
756 $scan_report->configs['always_send'] = $module_data['always_send'];
757 }
758 if ( ! empty( $module_data['report_subscribers'] ) ) {
759 // Reset all recipients before.
760 $scan_report->in_house_recipients = array();
761 $scan_report->out_house_recipients = array();
762 foreach ( $module_data['report_subscribers'] as $key => $subscribers ) {
763 $scan_report->$key = $subscribers;
764 }
765 }
766 // @since 2.7.0 Remove 'dry_run'-restoring.
767 // @since 2.7.0 scheduled values. Step#1 if 'report'-key exists.
768 if ( $this->is_pro && isset( $module_data['report'] ) ) {
769 $scan_settings->scheduled_scanning = 'enabled' === $module_data['report'];
770 }
771 // Step#2 if 'scheduled_scanning'-key exists.
772 if ( isset( $module_data['scheduled_scanning'] ) ) {
773 $scan_settings->scheduled_scanning = $module_data['scheduled_scanning'];
774 }
775 // We update the values in Model_Scan and Malware_Report so that the values are synchronized in both models.
776 if ( isset( $module_data['frequency'] ) ) {
777 $scan_settings->frequency = $module_data['frequency'];
778 $scan_report->frequency = $module_data['frequency'];
779 }
780 if ( isset( $module_data['day'] ) ) {
781 $scan_settings->day = $module_data['day'];
782 $scan_report->day = $module_data['day'];
783 }
784 if ( isset( $module_data['day_n'] ) ) {
785 $scan_settings->day_n = $module_data['day_n'];
786 $scan_report->day_n = $module_data['day_n'];
787 }
788 if ( isset( $module_data['time'] ) ) {
789 $scan_settings->time = $module_data['time'];
790 $scan_report->time = $module_data['time'];
791 }
792 $scan_report->save();
793 $scan_settings->save();
794 } else {
795 // Default data for scan notification.
796 $scan_notification->status = 'disabled';
797 // @since 2.7.0 We can remove it in the next version.
798 $scan_notification->dry_run = false;
799 $scan_notification->save();
800 // For scan report.
801 $scan_report->status = 'disabled';
802 // @since 2.7.0 We can remove it in the next version.
803 $scan_report->dry_run = false;
804 $scan_report->save();
805 }
806 } elseif ( 'iplockout' === $module ) {
807 // Run Lockout submodules.
808 foreach ( $controller as $lockout_controller ) {
809 $lockout_controller->import_data( $module_data );
810 }
811 // For Notification and Report.
812 if ( ! empty( $module_data ) ) {
813 // Get string values for notification & report.
814 if ( isset( $module_data['notification'] ) ) {
815 $lockout_notification = new Firewall_Notification();
816 if ( $lockout_notification->status !== $module_data['notification'] ) {
817 $lockout_notification->status = $module_data['notification'];
818 }
819 if ( isset( $module_data['login_lockout_notification'] ) ) {
820 $lockout_notification->configs['login_lockout'] = $module_data['login_lockout_notification'];
821 }
822 if ( isset( $module_data['ip_lockout_notification'] ) ) {
823 $lockout_notification->configs['nf_lockout'] = $module_data['ip_lockout_notification'];
824 }
825 if ( isset( $module_data['cooldown_enabled'] ) ) {
826 $lockout_notification->configs['limit'] = $module_data['cooldown_enabled'];
827 }
828 if ( isset( $module_data['cooldown_number_lockout'] ) ) {
829 $lockout_notification->configs['threshold'] = $module_data['cooldown_number_lockout'];
830 }
831 if ( isset( $module_data['cooldown_period'] ) ) {
832 $lockout_notification->configs['cool_off'] = $module_data['cooldown_period'];
833 }
834 if ( ! empty( $module_data['notification_subscribers'] ) ) {
835 // Reset all recipients before.
836 $lockout_notification->in_house_recipients = array();
837 $lockout_notification->out_house_recipients = array();
838 foreach ( $module_data['notification_subscribers'] as $key => $subscribers ) {
839 $lockout_notification->$key = $subscribers;
840 }
841 }
842
843 $lockout_notification->save();
844 }
845 if ( $this->is_pro && isset( $module_data['report'] ) ) {
846 $lockout_report = new Firewall_Report();
847 if ( $lockout_report->status !== $module_data['report'] ) {
848 $lockout_report->status = $module_data['report'];
849 }
850 if ( isset( $module_data['day'] ) ) {
851 $lockout_report->day = $module_data['day'];
852 }
853 if ( isset( $module_data['report_frequency'] ) ) {
854 $lockout_report->frequency = $module_data['report_frequency'];
855 }
856 if ( isset( $module_data['day_n'] ) ) {
857 $lockout_report->day_n = $module_data['day_n'];
858 }
859 if ( isset( $module_data['report_time'] ) ) {
860 $lockout_report->time = $module_data['report_time'];
861 }
862 if ( ! empty( $module_data['report_subscribers'] ) ) {
863 // Reset all recipients before.
864 $lockout_report->in_house_recipients = array();
865 $lockout_report->out_house_recipients = array();
866 foreach ( $module_data['report_subscribers'] as $key => $subscribers ) {
867 $lockout_report->$key = $subscribers;
868 }
869 }
870 if ( isset( $module_data['last_sent'] ) ) {
871 $lockout_report->last_sent = $module_data['last_sent'];
872 }
873 // @since 2.7.0 Remove 'dry_run'-restoring.
874 $lockout_report->save();
875 }
876 } else {
877 // Default data for lockout notification.
878 $lockout_notification = new Firewall_Notification();
879 $lockout_notification->status = 'disabled';
880 // @since 2.7.0 We can remove it in the next version.
881 $lockout_notification->dry_run = false;
882 $lockout_notification->configs = array(
883 'login_lockout' => false,
884 'nf_lockout' => false,
885 'limit' => false,
886 'threshold' => 3,
887 'cool_off' => 24,
888 );
889 $lockout_notification->save();
890 // For lockout report.
891 $lockout_report = new Firewall_Report();
892 $lockout_report->status = 'disabled';
893 // @since 2.7.0 We can remove it in the next version.
894 $lockout_report->dry_run = false;
895 $lockout_report->frequency = 'weekly';
896 $lockout_report->day_n = '1';
897 $lockout_report->day = 'sunday';
898 $lockout_report->time = '4:00';
899 $lockout_report->save();
900 }
901 } elseif ( 'audit' === $module ) {
902 // Import.
903 $controller->import_data( $module_data );
904 // Report.
905 $audit_report = new Audit_Report();
906 if ( ! empty( $module_data ) ) {
907 if (
908 $this->is_pro
909 && isset( $module_data['report'] )
910 && $audit_report->status !== $module_data['report']
911 ) {
912 $audit_report->status = $module_data['report'];
913 }
914 if ( isset( $module_data['frequency'] ) ) {
915 $audit_report->frequency = $module_data['frequency'];
916 }
917 if ( isset( $module_data['day_n'] ) ) {
918 $audit_report->day_n = $module_data['day_n'];
919 }
920 if ( isset( $module_data['day'] ) ) {
921 $audit_report->day = $module_data['day'];
922 }
923 if ( isset( $module_data['time'] ) ) {
924 $audit_report->time = $module_data['time'];
925 }
926 if ( ! empty( $module_data['subscribers'] ) ) {
927 // Reset all recipients before.
928 $audit_report->in_house_recipients = array();
929 $audit_report->out_house_recipients = array();
930 foreach ( $module_data['subscribers'] as $key => $subscribers ) {
931 $audit_report->$key = $subscribers;
932 }
933 }
934 if ( isset( $module_data['last_sent'] ) ) {
935 $audit_report->last_sent = $module_data['last_sent'];
936 }
937 // @since 2.7.0 Remove 'dry_run'-restoring.
938 } else {
939 // For audit report.
940 $audit_report->status = 'disabled';
941 // @since 2.7.0 We can remove it in the next version.
942 $audit_report->dry_run = false;
943 $audit_report->frequency = 'weekly';
944 $audit_report->day_n = '1';
945 $audit_report->day = 'sunday';
946 $audit_report->time = '4:00';
947 }
948
949 $audit_report->save();
950 /**
951 * If 'blocklist_monitor' module is activated, 'enabled' set as true.
952 */
953 } elseif (
954 'blocklist_monitor' === $module
955 && $this->is_pro
956 && isset( $module_data['status'] )
957 ) {
958 // No need to import data. Just change status.
959 ( new Blocklist_Monitor() )->change_status( $module_data['status'] );
960 } elseif (
961 'pwned_passwords' === $module
962 && isset( $module_data['custom_message'] )
963 ) {
964 $controller->import_data( $module_data );
965 } elseif ( 'two_factor' === $module ) {
966 $controller->import_data( $module_data );
967 } elseif ( 'mask_login' === $module ) {
968 $controller->import_data( $module_data );
969 } elseif ( 'security_headers' === $module ) {
970 $controller->import_data( $module_data );
971 } elseif ( 'settings' === $module ) {
972 $controller->import_data( $module_data );
973 } elseif ( 'security_tweaks' === $module ) {
974 // Import.
975 $controller->import_data( $module_data );
976 // Automate process.
977 if ( 'migration' !== $request_reason ) {
978 // There is some tweaks that require re-login. Not display error message during since 2.8.1 because it breaks the config flow on the Hub.
979 // If this is combined with mask login, then we need to redirect to new URL.
980 // The automate function should return this.
981 $tweak_class = wd_di()->get( Controller_Security_Tweaks::class );
982 $need_reauth = $tweak_class->automate( $module_data, $request_reason );
983 }
984 // For Tweak notification.
985 if ( ! empty( $module_data ) && isset( $module_data['notification'] ) ) {
986 $tweak_notification = new Tweak_Reminder();
987 if ( $tweak_notification->status !== $module_data['notification'] ) {
988 $tweak_notification->status = $module_data['notification'];
989 }
990
991 if ( isset( $module_data['notification_repeat'] ) ) {
992 // Temporary check for older versions.
993 if ( is_bool( $module_data['notification_repeat'] ) ) {
994 $tweak_notification->configs['reminder'] = $module_data['notification_repeat']
995 ? 'daily'
996 : 'weekly';
997 } elseif (
998 is_string( $module_data['notification_repeat'] )
999 && in_array(
1000 $module_data['notification_repeat'],
1001 array(
1002 'daily',
1003 'weekly',
1004 'monthly',
1005 ),
1006 true
1007 )
1008 ) {
1009 $tweak_notification->configs['reminder'] = $module_data['notification_repeat'];
1010 } else {
1011 $tweak_notification->configs['reminder'] = 'weekly';
1012 }
1013 } else {
1014 $tweak_notification->configs['reminder'] = 'weekly';
1015 }
1016 if ( ! empty( $module_data['subscribers'] ) ) {
1017 // Reset all recipients before.
1018 $tweak_notification->in_house_recipients = array();
1019 $tweak_notification->out_house_recipients = array();
1020 foreach ( $module_data['subscribers'] as $key => $subscribers ) {
1021 $tweak_notification->$key = $subscribers;
1022 }
1023 }
1024 if ( isset( $module_data['last_sent'] ) ) {
1025 $tweak_notification->last_sent = $module_data['last_sent'];
1026 }
1027 $tweak_notification->save();
1028 }
1029 }
1030 }
1031 }
1032 // We should disable quick setup.
1033 update_site_option( 'wp_defender_is_activated', 1 );
1034
1035 return $need_reauth;
1036 }
1037
1038 /**
1039 * Parses data for import, preparing it for use in the system.
1040 *
1041 * @param null|array $configs Optional. The configurations to parse. If not provided, gathers current data.
1042 *
1043 * @return array Returns an array containing parsed data for import.
1044 */
1045 public function parse_data_for_import( $configs = null ): array {
1046 if ( empty( $configs ) ) {
1047 $configs = $this->gather_data();
1048 }
1049 $strings = array();
1050 foreach ( $configs as $module => $module_data ) {
1051 $controller = $this->module_to_controller( $module, false );
1052 if ( ! is_object( $controller ) ) {
1053 // In free, when audit not present.
1054 $strings[ $module ][] = sprintf(
1055 /* translators: %s: Html for Pro-tag. */
1056 esc_html__( 'Inactive %s', 'defender-security' ),
1057 '<span class="sui-tag sui-tag-pro">Pro</span>'
1058 );
1059 continue;
1060 }
1061 $strings[ $module ] = $controller->export_strings();
1062 }
1063
1064 return array(
1065 'configs' => $configs,
1066 'strings' => $strings,
1067 );
1068 }
1069
1070 /**
1071 * Converts a module name to its corresponding controller object.
1072 *
1073 * @param string $module The name of the module.
1074 * @param bool $all_firewall_lockouts Whether to return all firewall and lockout controllers.
1075 *
1076 * @return object|array|string Returns the corresponding controller object, an array of controllers, or an empty
1077 * string if not applicable.
1078 */
1079 public function module_to_controller( string $module, bool $all_firewall_lockouts = false ) {
1080 switch ( $module ) {
1081 case 'security_tweaks':
1082 return new Controller_Security_Tweaks();
1083 case 'scan':
1084 return new Controller_Scan();
1085 case 'audit':
1086 return class_exists( Audit_Logging::class ) ? new Audit_Logging() : '';
1087 case 'iplockout':
1088 if ( $all_firewall_lockouts ) {
1089 return array(
1090 new Controller_Firewall(),
1091 new \WP_Defender\Controller\Login_Lockout(),
1092 new Nf_Lockout(),
1093 new Global_Ip(),
1094 new Blacklist(),
1095 new Controller_Ua_Lockout(),
1096 wd_di()->get( \WP_Defender\Controller\Antibot_Global_Firewall::class ),
1097 );
1098 } else {
1099 return new Controller_Firewall();
1100 }
1101 case 'settings':
1102 return new Main_Setting();
1103 case 'two_factor':
1104 return new Two_Factor();
1105 case 'mask_login':
1106 return new Controller_Mask_Login();
1107 case 'security_headers':
1108 return new Security_Headers();
1109 case 'blocklist_monitor':
1110 return new Blocklist_Monitor();
1111 case 'pwned_passwords':
1112 return new Controller_Password_Protection();
1113 default:
1114 return '';
1115 }
1116 }
1117
1118 /**
1119 * Checks if the provided configuration data has the new structure.
1120 *
1121 * @param array $configs The configuration data to check.
1122 *
1123 * @return bool Returns true if the new structure is used, otherwise false.
1124 */
1125 public function check_for_new_structure( array $configs ): bool {
1126 return array_key_exists( 'subscribers', $configs['security_tweaks'] );
1127 }
1128
1129 /**
1130 * Verifies the integrity of configuration data.
1131 *
1132 * @param array $data The configuration data to verify.
1133 *
1134 * @return bool Returns true if the data is valid, otherwise false.
1135 */
1136 public function verify_config_data( $data ): bool {
1137 if ( ! isset( $data['name'], $data['configs'], $data['strings'] )
1138 || empty( $data['name'] ) || empty( $data['strings'] )
1139 ) {
1140 return false;
1141 }
1142
1143 return true;
1144 }
1145
1146 /**
1147 * Imports module strings for use in the system.
1148 *
1149 * @param array $data The data containing module strings to import.
1150 *
1151 * @return array Returns an array of imported module strings.
1152 */
1153 public function import_module_strings( array $data ): array {
1154 if ( empty( $data['strings'] ) ) {
1155 return array();
1156 }
1157
1158 foreach ( $data['configs'] as $key => $config ) {
1159 if (
1160 'security_tweaks' === $key
1161 && 'enabled' === $config['notification']
1162 && 1 === ( is_array( $data['strings']['security_tweaks'] )
1163 || $data['strings']['security_tweaks'] instanceof Countable ? count( $data['strings']['security_tweaks'] ) : 0 )
1164 ) {
1165 $data['strings']['security_tweaks'][] = esc_html__( 'Email notifications active', 'defender-security' );
1166 } elseif ( 'scan' === $key ) {
1167 $data['strings']['scan'] = wd_di()->get( Controller_Scan::class )->config_strings(
1168 $config,
1169 $this->is_pro
1170 );
1171 } elseif ( 'iplockout' === $key ) {
1172 $data['strings']['iplockout'] = wd_di()->get( Controller_Firewall::class )->config_strings(
1173 $config,
1174 $this->is_pro
1175 );
1176 } elseif ( 'audit' === $key ) {
1177 // Additional check for Free version.
1178 $config = is_array( $config ) ? $config : array();
1179 $data['strings']['audit'] = wd_di()->get( Audit_Logging::class )->config_strings(
1180 $config,
1181 $this->is_pro
1182 );
1183 } elseif ( 'blocklist_monitor' === $key ) {
1184 // Additional check for Free version.
1185 $config = is_array( $config ) ? $config : array();
1186 $data['strings']['blocklist_monitor'] = $this->format_blocklist_monitor_strings(
1187 $config,
1188 $this->is_pro
1189 );
1190 } elseif ( 'pwned_passwords' === $key ) {
1191 $data['strings']['pwned_passwords'] = wd_di()->get( Controller_Password_Protection::class )->config_strings(
1192 $config,
1193 $this->is_pro
1194 );
1195 }
1196 }
1197
1198 return $data['strings'];
1199 }
1200
1201 /**
1202 * Creates default module strings for the Basic configuration.
1203 *
1204 * @param array $configs The configuration data.
1205 * @param bool $is_pro Whether the current installation is a pro version.
1206 *
1207 * @return array Returns an array of default module strings.
1208 */
1209 public function create_default_module_strings( array $configs, bool $is_pro ): array {
1210 $strings = array();
1211 foreach ( $configs as $key => $config ) {
1212 if ( 'security_tweaks' === $key ) {
1213 $strings['security_tweaks'] = wd_di()->get( Controller_Security_Tweaks::class )->config_strings(
1214 $config,
1215 $is_pro
1216 );
1217 } elseif ( 'scan' === $key ) {
1218 $strings['scan'] = wd_di()->get( Controller_Scan::class )->config_strings( $config, $is_pro );
1219 } elseif ( 'iplockout' === $key ) {
1220 $strings['iplockout'] = wd_di()->get( Controller_Firewall::class )->config_strings( $config, $is_pro );
1221 } elseif ( 'audit' === $key ) {
1222 $strings['audit'] = wd_di()->get( Audit_Logging::class )->config_strings( $config, $is_pro );
1223 } elseif ( 'two_factor' === $key ) {
1224 $strings['two_factor'][] = esc_html__( 'Inactive', 'defender-security' );
1225 } elseif ( 'mask_login' === $key ) {
1226 $strings['mask_login'][] = esc_html__( 'Inactive', 'defender-security' );
1227 } elseif ( 'security_headers' === $key ) {
1228 $strings['security_headers'][] = esc_html__( 'Active', 'defender-security' );
1229 } elseif ( 'blocklist_monitor' === $key ) {
1230 $strings['blocklist_monitor'] = wd_di()->get( Blocklist_Monitor::class )->config_strings(
1231 $config,
1232 $is_pro
1233 );
1234 } elseif ( 'pwned_passwords' === $key ) {
1235 $strings['pwned_passwords'][] = esc_html__( 'Inactive', 'defender-security' );
1236 }
1237 }
1238
1239 return $strings;
1240 }
1241
1242 /**
1243 * Retrieves a decoded settings array from the options table.
1244 *
1245 * @param string $key The key under which the settings are stored.
1246 *
1247 * @return array Returns the decoded settings array.
1248 */
1249 private function get_decoded_settings( string $key ): array {
1250 $data = get_site_option( $key );
1251 if ( $data && is_string( $data ) ) {
1252 $decoded_data = json_decode( $data, true );
1253 if ( is_array( $decoded_data ) ) {
1254 return $decoded_data;
1255 }
1256 }
1257
1258 return array();
1259 }
1260
1261 /**
1262 * Get settings of previous version.
1263 *
1264 * @return array
1265 */
1266 private function get_prev_settings(): array {
1267 $arr = array();
1268 if ( $this->is_pro ) {
1269 $status = (string) wd_di()->get( Blocklist_Monitor::class )->get_status();
1270 $arr['enabled'] = '1' === $status;
1271 $arr['status'] = $status;
1272 } else {
1273 $arr['enabled'] = false;
1274 }
1275
1276 return array(
1277 // Different keys.
1278 'security_tweaks' => $this->get_decoded_settings( 'wd_hardener_settings' ),
1279 'iplockout' => $this->get_decoded_settings( 'wd_lockdown_settings' ),
1280 // Identical keys.
1281 'scan' => $this->get_decoded_settings( 'wd_scan_settings' ),
1282 'audit' => $this->get_decoded_settings( 'wd_audit_settings' ),
1283 'two_factor' => $this->get_decoded_settings( 'wd_2auth_settings' ),
1284 'mask_login' => $this->get_decoded_settings( 'wd_masking_login_settings' ),
1285 'security_headers' => $this->get_decoded_settings( 'wd_security_headers_settings' ),
1286 'settings' => $this->get_decoded_settings( 'wd_main_settings' ),
1287 'blocklist_monitor' => $arr,
1288 );
1289 }
1290
1291 /**
1292 * Converts a module name to a human-readable name.
1293 *
1294 * @param string $module The module name to convert.
1295 *
1296 * @return string Returns the human-readable name of the module.
1297 */
1298 private function module_to_name( string $module ): string {
1299 switch ( $module ) {
1300 case 'security_tweaks':
1301 return esc_html__( 'Security Recommendations', 'defender-security' );
1302 case 'scan':
1303 return esc_html__( 'Malware Scanning', 'defender-security' );
1304 case 'audit':
1305 return Model_Audit_Logging::get_module_name();
1306 case 'iplockout':
1307 return esc_html__( 'Firewall', 'defender-security' );
1308 case 'settings':
1309 return esc_html__( 'Settings', 'defender-security' );
1310 case 'two_factor':
1311 return esc_html__( '2FA', 'defender-security' );
1312 case 'mask_login':
1313 return Model_Mask_Login::get_module_name();
1314 case 'security_headers':
1315 return Model_Security_Headers::get_module_name();
1316 case 'blocklist_monitor':
1317 return esc_html__( 'Blocklist Monitor', 'defender-security' );
1318 case 'pwned_passwords':
1319 return Model_Password_Protection::get_module_name();
1320 default:
1321 return '';
1322 }
1323 }
1324
1325 /**
1326 * Retrieves labels for a specific module.
1327 *
1328 * @param string $module The module for which to retrieve labels.
1329 *
1330 * @return array|void Returns an array of labels for the module, or void if the module does not exist.
1331 */
1332 public function model_labels( string $module ) {
1333 switch ( $module ) {
1334 case 'security_tweaks':
1335 return array_merge(
1336 ( new Model_Security_Tweaks() )->labels(),
1337 ( new Tweak_Reminder() )->labels()
1338 );
1339 case 'scan':
1340 return array_merge(
1341 ( new Model_Scan() )->labels(),
1342 ( new Malware_Notification() )->labels(),
1343 ( new Malware_Report() )->labels()
1344 );
1345 case 'audit':
1346 if ( class_exists( Model_Audit_Logging::class ) ) {
1347 return array_merge(
1348 ( new Model_Audit_Logging() )->labels(),
1349 ( new Audit_Report() )->labels()
1350 );
1351 }
1352 break;
1353 case 'iplockout':
1354 return array_merge(
1355 ( new Model_Firewall() )->labels(),
1356 ( new Model_Login_Lockout() )->labels(),
1357 ( new Model_Notfound_Lockout() )->labels(),
1358 ( new Model_Blacklist_Lockout() )->labels(),
1359 ( new Firewall_Notification() )->labels(),
1360 ( new Firewall_Report() )->labels(),
1361 ( new Model_Ua_Lockout() )->labels()
1362 );
1363 case 'settings':
1364 return ( new Model_Main_Setting() )->labels();
1365 case 'two_factor':
1366 return ( new Model_Two_Fa() )->labels();
1367 case 'mask_login':
1368 return ( new Model_Mask_Login() )->labels();
1369 case 'security_headers':
1370 return ( new Model_Security_Headers() )->labels();
1371 case 'blocklist_monitor':
1372 // Separate method is not for this model.
1373 return ( new Blocklist_Monitor() )->labels();
1374 case 'pwned_passwords':
1375 return ( new Model_Password_Protection() )->labels();
1376 default:
1377 break;
1378 }
1379 }
1380
1381 /**
1382 * Parses data for display on the Hub.
1383 *
1384 * @return array Returns an array containing parsed data for the Hub.
1385 */
1386 public function parse_data_for_hub(): array {
1387 $configs = $this->gather_data();
1388 $labels = array();
1389 $strings = array();
1390 foreach ( $configs as $module => $module_data ) {
1391 $model_labels = $this->model_labels( $module );
1392
1393 if ( is_array( $module_data ) && is_array( $model_labels ) ) {
1394 $labels[ $module ]['name'] = $this->module_to_name( $module );
1395 foreach ( $module_data as $key => $value ) {
1396 // Todo: update logic to import/export whitelisted/blocklisted countries via maxmind_license_key.
1397 if ( in_array( $key, array( 'geoIP_db', 'geodb_path' ), true ) ) {
1398 continue;
1399 }
1400
1401 if ( array_key_exists( $key, $model_labels ) ) {
1402 $labels[ $module ]['value'][ $key ] = array(
1403 'name' => $model_labels[ $key ],
1404 'value' => $value,
1405 );
1406 }
1407 }
1408
1409 $controller = $this->module_to_controller( $module, false );
1410 if ( ! is_object( $controller ) ) {
1411 // In free, when audit not present.
1412 $strings[ $module ] = sprintf(
1413 /* translators: %s: Html for Pro-tag. */
1414 esc_html__( 'Inactive %s', 'defender-security' ),
1415 '<span class="sui-tag sui-tag-pro">Pro</span>'
1416 );
1417 continue;
1418 }
1419 $strings[ $module ] = $controller->export_strings();
1420 }
1421 }
1422
1423 return array(
1424 'configs' => $configs,
1425 'labels' => $labels,
1426 'strings' => $strings,
1427 );
1428 }
1429
1430 /**
1431 * Format strings of Block list Monitor config.
1432 *
1433 * @param array $config Saved config.
1434 * @param bool $is_pro User membership status.
1435 *
1436 * @return array
1437 */
1438 private function format_blocklist_monitor_strings( array $config, bool $is_pro ): array {
1439 // If Block list Monitor is enable.
1440 if ( isset( $config['status'] ) && '1' === (string) $config['status'] ) {
1441 if ( $is_pro ) {
1442 $monitor = array( esc_html__( 'Active', 'defender-security' ) );
1443 } else {
1444 $monitor = array(
1445 sprintf(
1446 /* translators: %s: Html for Pro-tag. */
1447 esc_html__( 'Active %s', 'defender-security' ),
1448 '<span class="sui-tag sui-tag-pro">Pro</span>'
1449 ),
1450 );
1451 }
1452 } elseif ( $is_pro ) {
1453 $monitor = array( esc_html__( 'Inactive', 'defender-security' ) );
1454 } else {
1455 $monitor = array(
1456 sprintf(
1457 /* translators: %s: Html for Pro-tag. */
1458 esc_html__( 'Inactive %s', 'defender-security' ),
1459 '<span class="sui-tag sui-tag-pro">Pro</span>'
1460 ),
1461 );
1462 }
1463
1464 return $monitor;
1465 }
1466
1467 /**
1468 * Prepares labels from configuration data.
1469 *
1470 * @param array $configs The configuration data.
1471 *
1472 * @return array Returns an array of prepared labels.
1473 */
1474 public function prepare_config_labels( array $configs ): array {
1475 $labels = array();
1476
1477 foreach ( $configs as $module => $module_data ) {
1478 $model_labels = $this->model_labels( $module );
1479
1480 if ( is_array( $module_data ) && is_array( $model_labels ) ) {
1481 $labels[ $module ]['name'] = $this->module_to_name( $module );
1482 foreach ( $module_data as $key => $value ) {
1483 // Todo: update logic to import/export whitelisted/blocklisted countries via maxmind_license_key.
1484 if ( in_array( $key, array( 'geoIP_db', 'geodb_path' ), true ) ) {
1485 continue;
1486 }
1487
1488 if ( array_key_exists( $key, $model_labels ) ) {
1489 $labels[ $module ]['value'][ $key ] = array(
1490 'name' => $model_labels[ $key ],
1491 'value' => $value,
1492 );
1493 }
1494 }
1495 }
1496 }
1497
1498 return $labels;
1499 }
1500
1501 /**
1502 * User enumeration options enabled list.
1503 *
1504 * @return array Return array of enabled user enumeration options.
1505 */
1506 private function get_enabled_user_enums() {
1507 $prevent_enum_users = new Prevent_Enum_Users();
1508
1509 return $prevent_enum_users->get_enabled_user_enums();
1510 }
1511
1512 /**
1513 * Security key all option.
1514 *
1515 * @return mixed All value of the "security key" feature of security tweak.
1516 */
1517 private function get_security_key_all_options() {
1518 $security_key = new Security_Key();
1519
1520 return $security_key->get_all_option();
1521 }
1522 }
1523