PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / controller / security-tweaks.php

security-tweaks.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/controller/security-tweaks.php

985 lines 24.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WP_Defender\Controller;
4
5 use Calotes\Component\Request;
6 use Calotes\Component\Response;
7 use WP_Defender\Component\Config\Config_Hub_Helper;
8 use WP_Defender\Component\Security_Tweaks\Servers\Server;
9 use Calotes\Helper\Array_Cache;
10 use WP_Defender\Component\Security_Tweaks\WP_Version;
11 use WP_Defender\Component\Security_Tweaks\Hide_Error;
12 use WP_Defender\Component\Security_Tweaks\PHP_Version;
13 use WP_Defender\Component\Security_Tweaks\Prevent_PHP;
14 use WP_Defender\Component\Security_Tweaks\Change_Admin;
15 use WP_Defender\Component\Security_Tweaks\Security_Key;
16 use WP_Defender\Component\Security_Tweaks\Login_Duration;
17 use WP_Defender\Component\Security_Tweaks\Disable_XML_RPC;
18 use WP_Defender\Component\Security_Tweaks\Disable_Trackback;
19 use WP_Defender\Component\Security_Tweaks\Prevent_Enum_Users;
20 use WP_Defender\Component\Security_Tweaks\Disable_File_Editor;
21 use WP_Defender\Component\Security_Tweaks\Protect_Information;
22 use WP_Defender\Controller;
23
24 class Security_Tweaks extends Controller {
25 public $slug = 'wdf-hardener';
26 /**
27 * @var \WP_Defender\Model\Setting\Security_Tweaks
28 */
29 protected $model;
30
31 /**
32 * @var \WP_Defender\Component\Scan
33 */
34 public $scan;
35
36 /**
37 * Components instance array.
38 *
39 * @var array
40 */
41 private $component_instances;
42
43 /**
44 * Instance of Security_Key.
45 *
46 * @var Security_Key
47 */
48 private $security_key;
49
50 /**
51 * Instance of Prevent_Enum_Users.
52 *
53 * @var Prevent_Enum_Users
54 */
55 private $prevent_enum_users;
56
57
58 public const STATUS_ISSUES = 'issues', STATUS_RESOLVE = 'fixed', STATUS_IGNORE = 'ignore', STATUS_RESTORE = 'restore';
59
60 public function __construct() {
61 $this->register_page(
62 esc_html__( 'Recommendations', 'wpdef' ),
63 $this->slug,
64 array(
65 &$this,
66 'main_view',
67 ),
68 $this->parent_slug
69 );
70 $this->model = wd_di()->get( \WP_Defender\Model\Setting\Security_Tweaks::class );
71 $this->register_routes();
72
73 // Init all the tweaks, should happen one time.
74 $this->component_instances = $this->init_tweaks();
75
76 $this->scan = wd_di()->get( \WP_Defender\Component\Scan::class );
77
78 $this->security_key = $this->component_instances['security-key'];
79 $this->prevent_enum_users = $this->component_instances['prevent-enum-users'];
80
81 // Now shield up.
82 $this->boot();
83 // Add addition hooks.
84 add_action( 'defender_enqueue_assets', array( &$this, 'enqueue_assets' ) );
85 add_action( 'wp_loaded', array( &$this, 'should_output_error' ) );
86 }
87
88 /**
89 * Dummy function for testing a check.
90 */
91 public function should_output_error() {
92 if ( ! isset( $_GET['defender_test_error_reporting'] ) ) {
93 return;
94 }
95
96 // It should be only trigger by admin.
97 if ( ! $this->check_permission() ) {
98 return;
99 }
100
101 $var = '$' . uniqid();
102 // This should output a warning.
103 echo ${$var};
104 exit();
105 }
106
107 /**
108 * Process.
109 * @param Request $request
110 *
111 * @return Response
112 * @defender_route
113 */
114 public function process( Request $request ) {
115 $data = $request->get_data(
116 array(
117 'slug' => array(
118 'type' => 'string',
119 'sanitize' => 'sanitize_text_field',
120 ),
121 'current_server' => array(
122 'type' => 'string',
123 'sanitize' => 'sanitize_text_field',
124 ),
125 )
126 );
127
128 $slug = $data['slug'] ?? false;
129 $tweak = $this->get_tweak( $slug );
130
131 if ( ! is_object( $tweak ) ) {
132 return new Response(
133 false,
134 array(
135 'message' => __( 'Invalid request', 'wpdef' ),
136 )
137 );
138 }
139
140 if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) {
141 $current_server = $data['current_server'] ?? false;
142 if ( ! $current_server ) {
143 return new Response(
144 false,
145 array(
146 'message' => __( 'Invalid request', 'wpdef' ),
147 )
148 );
149 }
150
151 $ret = $tweak->process( $current_server );
152 } else {
153 $ret = $tweak->process();
154 }
155
156 if ( true === $ret ) {
157 Config_Hub_Helper::set_clear_active_flag();
158 $this->model->mark( self::STATUS_RESOLVE, $slug );
159 $this->ajax_response( __( 'Security recommendation successfully resolved.', 'wpdef' ) );
160 }
161 if ( is_wp_error( $ret ) ) {
162 $this->ajax_response( $ret->get_error_message(), false );
163 }
164
165 return new Response(
166 false,
167 array(
168 'message' => __( 'Invalid request', 'wpdef' ),
169 )
170 );
171 }
172
173 /**
174 * Revert.
175 * @param Request $request
176 *
177 * @return Response
178 * @defender_route
179 */
180 public function revert( Request $request ) {
181 $data = $request->get_data(
182 array(
183 'slug' => array(
184 'type' => 'string',
185 'sanitize' => 'sanitize_text_field',
186 ),
187 'current_server' => array(
188 'type' => 'string',
189 'sanitize' => 'sanitize_text_field',
190 ),
191 )
192 );
193 $slug = $data['slug'] ?? false;
194 $tweak = $this->get_tweak( $slug );
195 $invalid = array( 'message' => __( 'Invalid request', 'wpdef' ) );
196 if ( ! is_object( $tweak ) ) {
197 return new Response(
198 false,
199 $invalid
200 );
201 }
202 if ( in_array( $slug, array( 'prevent-php-executed', 'protect-information' ), true ) ) {
203 $current_server = $data['current_server'] ?? false;
204 if ( ! $current_server ) {
205 return new Response(
206 false,
207 $invalid
208 );
209 }
210 $ret = $tweak->revert( $current_server );
211 } else {
212 $ret = $tweak->revert();
213 }
214
215 if ( is_wp_error( $ret ) ) {
216 $this->ajax_response( $ret->get_error_message(), false );
217 }
218 if ( true === $ret ) {
219 Config_Hub_Helper::set_clear_active_flag();
220 $this->model->mark( self::STATUS_ISSUES, $slug );
221 $this->ajax_response( __( 'Security recommendation successfully reverted.', 'wpdef' ) );
222 }
223
224 return new Response(
225 false,
226 $invalid
227 );
228 }
229
230 /**
231 * Ignore.
232 * @param Request $request
233 *
234 * @return Response
235 * @defender_route
236 */
237 public function ignore( Request $request ) {
238 $data = $request->get_data(
239 array(
240 'slug' => array(
241 'type' => 'string',
242 'sanitize' => 'sanitize_text_field',
243 ),
244 )
245 );
246 $slug = $data['slug'] ?? false;
247 $tweak = $this->get_tweak( $slug );
248 if ( ! is_object( $tweak ) ) {
249 return new Response(
250 false,
251 array(
252 'message' => __( 'Invalid request', 'wpdef' ),
253 )
254 );
255 }
256 $this->model->mark( self::STATUS_IGNORE, $slug );
257
258 $this->security_key->cron_unschedule();
259
260 $this->ajax_response( __( 'Security recommendation successfully ignored.', 'wpdef' ) );
261 }
262
263 /**
264 * Restore.
265 *
266 * @defender_route
267 */
268 public function restore( Request $request ) {
269 $data = $request->get_data(
270 array(
271 'slug' => array(
272 'type' => 'string',
273 'sanitize' => 'sanitize_text_field',
274 ),
275 )
276 );
277 $slug = $data['slug'] ?? false;
278 $tweak = $this->get_tweak( $slug );
279 if ( ! is_object( $tweak ) ) {
280 return new Response(
281 false,
282 array(
283 'message' => __( 'Invalid request', 'wpdef' ),
284 )
285 );
286 }
287 $this->model->mark( self::STATUS_RESTORE, $slug );
288
289 if ( $this->security_key->get_is_autogenerate_keys() ) {
290 // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job.
291 $this->security_key->cron_unschedule();
292 $this->security_key->cron_schedule();
293 }
294
295 $this->ajax_response( __( 'Security recommendation successfully restored.', 'wpdef' ) );
296 }
297
298 /**
299 * Recheck.
300 * @param Request $request
301 *
302 * @return Response
303 * @defender_route
304 */
305 public function recheck( Request $request ) {
306 $data = $request->get_data(
307 array(
308 'slug' => array(
309 'type' => 'string',
310 'sanitize' => 'sanitize_text_field',
311 ),
312 )
313 );
314 $slug = $data['slug'] ?? false;
315 $tweak = $this->get_tweak( $slug );
316
317 if ( ! is_object( $tweak ) ) {
318 return new Response(
319 false,
320 array(
321 'message' => __( 'Invalid request', 'wpdef' ),
322 )
323 );
324 }
325
326 $ret = $tweak->check();
327
328 if ( true === $ret ) {
329 $this->ajax_response( __( 'Security recommendation successfully re-checked.', 'wpdef' ), true, 1 );
330 }
331
332 if ( is_wp_error( $ret ) ) {
333 return new Response(
334 false,
335 array(
336 'message' => $ret->get_error_message(),
337 )
338 );
339 }
340
341 return new Response(
342 false,
343 array(
344 'message' => __( 'Invalid request', 'wpdef' ),
345 )
346 );
347 }
348
349 /**
350 * Update security reminder.
351 * @param Request $request
352 *
353 * @return Response
354 * @defender_route
355 */
356 public function update_security_reminder( Request $request ) {
357 $data = $request->get_data();
358 $remind_date = $data['remind_date'] ?? false;
359
360 $is_autogen_flag = isset( $data['is_autogenerate_keys'] ) ?
361 filter_var( $data['is_autogenerate_keys'], FILTER_VALIDATE_BOOLEAN ) :
362 false;
363
364 if ( ! $remind_date ) {
365 return new Response(
366 false,
367 array(
368 'message' => __( 'Invalid Reminder frequency', 'wpdef' ),
369 )
370 );
371 }
372
373 $values = array(
374 'reminder_duration' => $remind_date,
375 'reminder_date' => strtotime( '+' . $remind_date, current_time( 'timestamp' ) ),// phpcs:ignore
376 'is_autogenerate_keys' => $is_autogen_flag,
377 );
378
379 if ( update_site_option( 'defender_security_tweaks_' . $this->security_key->slug, $values ) ) {
380
381 if ( true === $is_autogen_flag ) {
382 // Mandatory: cron_schedule method bypass scheduling if already a schedule for this job.
383 $this->security_key->cron_unschedule();
384 $this->security_key->cron_schedule();
385 }
386
387 return new Response(
388 true,
389 array(
390 'message' => __( 'Security recommendation successfully updated.', 'wpdef' ),
391 )
392 );
393 } else {
394 return new Response(
395 false,
396 array(
397 'message' => __( 'Error while updating.', 'wpdef' ),
398 )
399 );
400 }
401 }
402
403 /**
404 * @param string $message
405 * @param bool $is_success
406 * @param bool|int $interval
407 *
408 * @return Response
409 */
410 private function ajax_response( $message, $is_success = true, $interval = false ) {
411 global $wp_version;
412 $settings = new \WP_Defender\Model\Setting\Security_Tweaks();
413 $data = array(
414 'message' => $message,
415 'summary' => array(
416 'issues_count' => count( $settings->issues ),
417 'fixed_count' => count( $settings->fixed ),
418 'ignore_count' => count( $settings->ignore ),
419 'php_version' => phpversion(),
420 'wp_version' => $wp_version,
421 ),
422 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ),
423 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ),
424 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ),
425 'issues_slugs' => $settings->issues,
426 );
427 if ( $interval ) {
428 $data['interval'] = $interval;
429 }
430
431 return new Response( $is_success, $data );
432 }
433
434 /**
435 * Output necessary data on frontend.
436 */
437 public function enqueue_assets() {
438 if ( ! $this->is_page_active() ) {
439 return;
440 }
441
442 wp_localize_script( 'def-securitytweaks', 'security_tweaks', $this->data_frontend() );
443 wp_enqueue_script( 'def-securitytweaks' );
444 $this->enqueue_main_assets();
445 }
446
447 public function data_frontend() {
448 $this->refresh_tweaks_status();
449 global $wp_version;
450
451 $not_allowed_bulk = array(
452 'php-version',
453 'replace-admin-username',
454 );
455 if ( 'nginx' === Server::get_current_server() ) {
456 $not_allowed_bulk[] = 'protect-information';
457 $not_allowed_bulk[] = 'prevent-php-executed';
458 }
459 $data = array(
460 'summary' => array(
461 'fixed_count' => count( $this->model->fixed ),
462 'ignore_count' => count( $this->model->ignore ),
463 'issues_count' => count( $this->model->issues ),
464 'php_version' => phpversion(),
465 'wp_version' => $wp_version,
466 ),
467 'issues' => $this->init_tweaks( self::STATUS_ISSUES, 'array' ),
468 'fixed' => $this->init_tweaks( self::STATUS_RESOLVE, 'array' ),
469 'ignored' => $this->init_tweaks( self::STATUS_IGNORE, 'array' ),
470 'not_allowed_bulk' => $not_allowed_bulk,
471 'indicator_issue_count' => $this->scan->indicator_issue_count(),
472 'is_autogenerate_keys' => $this->security_key->get_is_autogenerate_keys(),
473 'reminder_frequencies' => $this->security_key->reminder_frequencies(),
474 'enabled_user_enums' => $this->prevent_enum_users->get_enabled_user_enums(),
475 );
476
477 return array_merge( $data, $this->dump_routes_and_nonces() );
478 }
479
480 public function main_view() {
481 $this->render( 'main' );
482 }
483
484 /**
485 * @param Request $request
486 *
487 * @return Response
488 * @defender_route
489 */
490 public function bulk_hub( Request $request ) {
491 $data = $request->get_data(
492 array(
493 'slugs' => array(
494 'type' => 'array',
495 'sanitize' => 'sanitize_text_field',
496 ),
497 'intention' => array(
498 'type' => 'string',
499 'sanitize' => 'sanitize_text_field',
500 ),
501 )
502 );
503 $slugs = $data['slugs'] ?? array();
504 $intention = $data['intention'] ?? false;
505 // Get processed and unprocessed tweaks.
506 [$processed, $unprocessed] = $this->security_tweaks_auto_action( $slugs, $intention );
507
508 $message = sprintf(
509 /* translators: ... */
510 __( 'You have bulk %1$s %2$s security recommendations.', 'wpdef' ),
511 'ignore' === $intention ? 'ignored' : 'resolved',
512 $processed
513 );
514
515 if ( isset( $unprocessed ) && $unprocessed > 0 ) {
516 // If we have this case this mean the intention is resolved.
517 $message = sprintf(
518 /* translators: ... */
519 __(
520 'You have bulk actioned %d security recommendations. You still have a few unresolved security recommendations, which cannot be bulk actioned automatically, so please address them below.',
521 'wpdef'
522 ),
523 $processed
524 );
525
526 Config_Hub_Helper::set_clear_active_flag();
527 }
528 $this->ajax_response( $message );
529 }
530
531 /**
532 * Mass processing.
533 *
534 * @param array $slugs
535 * @param string $intention
536 *
537 * @return array
538 */
539 public function security_tweaks_auto_action( $slugs, $intention ) {
540 $processed = 0;
541 $unprocessed = 0;
542
543 foreach ( $slugs as $slug ) {
544 $tweak = $this->get_tweak( $slug );
545 if ( 'ignore' === $intention ) {
546 $this->model->mark( self::STATUS_IGNORE, $slug );
547 } elseif ( 'resolve' === $intention ) {
548 $wont_do = array(
549 'replace-admin-username',
550 'prevent-php-executed',
551 'wp-version',
552 'php-version',
553 'protect-information',
554 );
555 if ( in_array( $slug, $wont_do, true ) ) {
556 $unprocessed += 1;
557 continue;
558 }
559 if ( $tweak->has_method( 'bulk_process' ) ) {
560 $ret = $tweak->bulk_process();
561 } else {
562 $ret = $tweak->process();
563 }
564 if ( is_wp_error( $ret ) ) {
565 $data = $tweak->to_array();
566 $this->ajax_response(
567 sprintf(
568 /* translators: ... */
569 __(
570 'There is an error while processing recommendation %1$s, error message: %2$s',
571 'wpdef'
572 ),
573 $data['title'],
574 $ret->get_error_message()
575 ),
576 false
577 );
578 }
579 $this->model->mark( self::STATUS_RESOLVE, $slug );
580 }
581 $processed ++;
582 }
583
584 return array( $processed, $unprocessed );
585 }
586
587 /**
588 * Refresh the tweak status and save their state.
589 *
590 * @return void
591 */
592 public function refresh_tweaks_status() {
593 $tweaks = $this->init_tweaks();
594 $settings = new \WP_Defender\Model\Setting\Security_Tweaks();
595 $fixed = array();
596 $issues = array();
597
598 foreach ( $tweaks as $slug => $class ) {
599 if ( $settings->is_tweak_ignore( $slug ) ) {
600 continue;
601 }
602
603 $is_resolved = $class->check();
604
605 if ( $is_resolved ) {
606 $fixed[] = $slug;
607 } else {
608 $issues[] = $slug;
609 }
610 }
611
612 $settings->fixed = $fixed;
613 $settings->issues = $issues;
614 $settings->save();
615 }
616
617 /**
618 * This function for shield every active tweaks up, we will use the cached result.
619 * No check function trigger in this init runtime.
620 */
621 private function boot() {
622 $tweaks = $this->init_tweaks( self::STATUS_RESOLVE );
623 foreach ( $tweaks as $tweak ) {
624 $tweak->shield_up();
625 }
626 }
627
628 /**
629 * Instance all the tweaks, happen one time in init runtime.
630 *
631 * @param null $type
632 * @param string $format Object for internal use, array for frontend use.
633 *
634 * @return array
635 */
636 public function init_tweaks( $type = null, $format = 'object' ) {
637 $classes = array(
638 Disable_XML_RPC::class,
639 WP_Version::class,
640 Hide_Error::class,
641 PHP_Version::class,
642 Change_Admin::class,
643 Security_Key::class,
644 Login_Duration::class,
645 Disable_Trackback::class,
646 Prevent_Enum_Users::class,
647 Disable_File_Editor::class,
648 );
649 if ( 'cli' !== php_sapi_name() ) {
650 // We don't load this in cli, as clearly no server is running.
651 $classes = array_merge(
652 $classes,
653 array(
654 Protect_Information::class,
655 Prevent_PHP::class,
656 )
657 );
658 }
659
660 $tweaks = Array_Cache::get( 'tweaks', 'tweaks' );
661
662 if ( ! is_array( $tweaks ) ) {
663 foreach ( $classes as $class ) {
664 $obj = new $class;
665 $tweaks[ $obj->slug ] = $obj;
666 }
667 Array_Cache::set( 'tweaks', $tweaks, 'tweaks' );
668 }
669 $tmp = array();
670 if ( is_null( $type ) ) {
671 $tmp = $tweaks;
672 } else {
673 $settings = new \WP_Defender\Model\Setting\Security_Tweaks();
674 $compare = $settings->$type;
675 foreach ( $compare as $slug ) {
676 if ( isset( $tweaks[ $slug ] ) ) {
677 $tmp[ $slug ] = $tweaks[ $slug ];
678 }
679 }
680 }
681
682 if ( 'array' === $format ) {
683 // We need to parse this as array.
684 foreach ( $tmp as $slug => $obj ) {
685 $arr = $obj->to_array();
686 $arr['status'] = $type;
687 $tmp[ $slug ] = $arr;
688 }
689 }
690
691 return $tmp;
692 }
693
694 /**
695 * @param $slug
696 *
697 * @return mixed|null
698 */
699 private function get_tweak( $slug ) {
700 $tweaks = Array_Cache::get( 'tweaks', 'tweaks' );
701
702 return $tweaks[ $slug ] ?? null;
703 }
704
705 /**
706 * A summary data for dashboard.
707 *
708 * @return array
709 */
710 public function to_array() {
711 $this->refresh_tweaks_status();
712 $settings = new \WP_Defender\Model\Setting\Security_Tweaks();
713
714 return array(
715 'rules' => array_slice( $this->init_tweaks( self::STATUS_ISSUES, 'array' ), 0, 5 ),
716 'count' => array(
717 'issues' => count( $settings->issues ),
718 'resolved' => count( $settings->fixed ),
719 'total' => count( $this->init_tweaks() ),
720 ),
721 );
722 }
723
724 public function remove_settings() {
725 // Revert it first.
726 $tweaks = $this->init_tweaks( self::STATUS_RESOLVE );
727 // Assign this so internal can use the current server.
728 $_POST['current_server'] = Server::get_current_server();
729 foreach ( $tweaks as $tweak ) {
730 $tweak->revert();
731
732 if ( method_exists( $tweak, 'delete_all_option' ) ) {
733 $tweak->delete_all_option();
734 }
735 }
736
737 ( new \WP_Defender\Model\Setting\Security_Tweaks() )->delete();
738
739 delete_site_transient( 'defender_current_server' );
740 delete_site_transient( 'defender_apache_version' );
741 wp_clear_scheduled_hook( 'wpdef_sec_key_gen' );
742 }
743
744 public function remove_data() {}
745
746 /**
747 * @param array $data
748 * @param string $request_reason
749 *
750 * @return bool|string
751 * @since 2.8.1 Add $request_reason param. If there's a request from Hub the plugin doesn't send the error message.
752 */
753 public function automate( $data, $request_reason ) {
754 $this->refresh_tweaks_status();
755 $need_reauth = false;
756 // Resolve tweaks.
757 if ( ! empty( $data['fixed'] ) ) {
758 // There are some tweak that need manual apply, as files based, or change admin.
759 $manual_done = array(
760 'replace-admin-username',
761 'prevent-php-executed',
762 'wp-version',
763 'php-version',
764 'protect-information',
765 );
766
767 $diff_keys = array_diff( $data['fixed'], $this->model->fixed, $manual_done );
768 if ( ! empty( $diff_keys ) ) {
769 foreach ( $diff_keys as $slug ) {
770 $tweak = $this->get_tweak( $slug );
771 if ( $tweak->has_method( 'bulk_process' ) ) {
772 $ret = $tweak->bulk_process();
773 } else {
774 $ret = $tweak->process();
775 }
776
777 if ( is_wp_error( $ret ) ) {
778 if ( 'hub' === $request_reason ) {
779 continue;
780 }
781 $data = $tweak->to_array();
782
783 return sprintf(
784 /* translators: ... */
785 __( 'There is an error while processing recommendation %1$s, error message: %2$s', 'wpdef' ),
786 $data['title'],
787 $ret->get_error_message()
788 );
789 }
790
791 $this->model->mark( self::STATUS_RESOLVE, $slug );
792 }
793 if ( in_array( ( new Security_Key )->slug, $diff_keys, true ) ) {
794 $need_reauth = true;
795 }
796 }
797 }
798 // Revert tweaks.
799 if ( ! empty( $data['issues'] ) ) {
800 $diff_keys = array_diff( $data['issues'], $this->model->issues );
801
802 if ( ! empty( $diff_keys ) ) {
803 // Issues.
804 foreach ( $diff_keys as $slug ) {
805 $tweak = $this->get_tweak( $slug );
806 $ret = $tweak->revert();
807 if ( is_wp_error( $ret ) ) {
808 if ( 'hub' === $request_reason ) {
809 continue;
810 }
811 $data = $tweak->to_array();
812
813 return sprintf(
814 /* translators: ... */
815 __( 'There is an error while processing recommendation %1$s, error message: %2$s', 'wpdef' ),
816 $data['title'],
817 $ret->get_error_message()
818 );
819 }
820 $this->model->mark( self::STATUS_ISSUES, $slug );
821 }
822 }
823 }
824 // Ignore tweaks.
825 if ( ! empty( $data['ignore'] ) ) {
826 $diff_keys = array_diff( $data['ignore'], $this->model->ignore );
827 if ( ! empty( $diff_keys ) ) {
828 foreach ( $diff_keys as $slug ) {
829 $this->model->mark( self::STATUS_IGNORE, $slug );
830 }
831 }
832 }
833
834 return $need_reauth;
835 }
836
837 public function import_data( $data ) {
838
839 $enabled_user_enums = array();
840
841 if ( isset( $data['enabled_user_enums'] ) ) {
842 $enabled_user_enums = (array) $data['enabled_user_enums'];
843
844 unset( $data['enabled_user_enums'] );
845 }
846
847 $this->prevent_enum_users
848 ->set_enabled_user_enums( $enabled_user_enums );
849
850 if ( ! empty( $data['security_key'] ) && is_array( $data['security_key'] ) ) {
851 $this->security_key->update_all_option( $data['security_key'] );
852 }
853
854 $model = new \WP_Defender\Model\Setting\Security_Tweaks();
855
856 $model->import( $data );
857 if ( $model->validate() ) {
858 $model->save();
859 }
860 }
861
862 public function export_strings() {
863 $this->refresh_tweaks_status();
864 $settings = new \WP_Defender\Model\Setting\Security_Tweaks();
865 $strings = array();
866 $count_all = count( $settings->fixed ) + count( $settings->issues ) + count( $settings->ignore );
867
868 if ( empty( $settings->issues ) ) {
869 $strings[] = __( 'All available recommendations activated', 'wpdef' );
870 } else {
871 $strings[] = sprintf(
872 /* translators: ... */
873 __( '%1$d/%2$d recommendations activated', 'wpdef' ),
874 count( $settings->fixed ),
875 $count_all
876 );
877 }
878
879 $tweak_notification = new \WP_Defender\Model\Notification\Tweak_Reminder();
880 if ( 'enabled' === $tweak_notification->status ) {
881 $strings[] = __( 'Email notifications active', 'wpdef' );
882 }
883
884 return $strings;
885 }
886
887 /**
888 * @param array $config
889 * @param bool $is_pro
890 *
891 * @return array
892 */
893 public function config_strings( $config, $is_pro ) {
894 $strings = [];
895 if ( empty( $config['issues'] ) ) {
896 $strings[] = __( 'All available recommendations activated', 'wpdef' );
897 } else {
898 $strings[] = sprintf(
899 /* translators: ... */
900 __( '%1$d/%2$d recommendations activated', 'wpdef' ),
901 is_array($config['fixed']) || $config['fixed'] instanceof \Countable ? count( $config['fixed'] ) : 0,
902 (is_array($config['fixed']) || $config['fixed'] instanceof \Countable ? count( $config['fixed'] ) : 0) + (is_array($config['issues']) || $config['issues'] instanceof \Countable ? count( $config['issues'] ) : 0) + (is_array($config['ignore']) || $config['ignore'] instanceof \Countable ? count( $config['ignore'] ) : 0)
903 );
904 }
905 if ( 'enabled' === $config['notification'] ) {
906 $strings[] = __( 'Email notifications active', 'wpdef' );
907 }
908
909 return $strings;
910 }
911
912 /**
913 * Update auto generate flag.
914 *
915 * @defender_route
916 */
917 public function update_autogenerate_flag( Request $request ) {
918 $data = $request->get_data();
919
920 $is_autogen_flag = isset( $data['is_autogenerate_keys'] ) ?
921 filter_var( $data['is_autogenerate_keys'], FILTER_VALIDATE_BOOLEAN ) :
922 false;
923
924 $is_success = false;
925 $message = __( 'An error occurred, try again.', 'wpdef' );
926
927 if ( $this->security_key->set_is_autogenrate_keys( $is_autogen_flag ) ) {
928 $is_success = true;
929
930 if ( $is_autogen_flag ) {
931 $this->security_key->cron_schedule();
932 $message = __( 'Security key/salt autogenerate enabled.', 'wpdef' );
933 } else {
934 $this->security_key->cron_unschedule();
935 $message = __( 'Security key/salt autogenerate disabled.', 'wpdef' );
936 }
937 }
938
939 return new Response(
940 $is_success,
941 array(
942 'message' => $message,
943 )
944 );
945
946 }
947
948 /**
949 * Get component security key instance.
950 *
951 * @return \WP_Defender\Component\Security_Tweaks\Security_Key
952 */
953 public function get_security_key() {
954 return $this->security_key;
955 }
956
957 /**
958 * Update enabled user enums list.
959 *
960 * @defender_route
961 */
962 public function update_enabled_user_enums( Request $request ) {
963 $data = (array) $request->get_data();
964
965 $enabled_user_enums = $data['enabled_user_enums'];
966
967 $is_success = false;
968 $message = __( 'An error occurred, try again.', 'wpdef' );
969
970 if ( $this->prevent_enum_users->set_enabled_user_enums( $enabled_user_enums ) ) {
971 $is_success = true;
972 $message = __( 'User enumeration option(s) updated successfully.', 'wpdef' );
973 }
974
975 return new Response(
976 $is_success,
977 array(
978 'message' => $message,
979 )
980 );
981
982 }
983
984 }
985