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

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