PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.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
← All changes | src/class-admin.php +203 -52 5.6.0 → 6.3.0 View file →
@@ -11,8 +11,9 @@
11 11 use WP_Defender\Behavior\WPMUDEV;
12 12 use WP_Defender\Component\Firewall;
13 13 use WP_Defender\Integrations\Dashboard_Whitelabel;
14 14 use WP_Defender\Component\Config\Config_Hub_Helper;
15 +use WP_Defender\Helper\Analytics\Deactivation_Survey;
15 16
16 17 if ( ! defined( 'ABSPATH' ) ) {
17 18 die();
18 19 }
@@ -37,9 +38,13 @@
37 38 public function __construct() {
38 39 $this->is_wp_org_version = defender_is_wp_org_version();
39 40 add_action( 'wp_ajax_defender_ip_detection_notice_dismiss', array( $this, 'dismiss_notice' ) );
40 41 add_action( 'wp_ajax_defender_ip_detection_switch_to_xff', array( $this, 'switch_to_xff' ) );
42 + add_action( 'wp_ajax_defender_track_deactivate', array( $this, 'track_deactivate' ) );
41 43 add_action( 'admin_head', array( $this, 'add_global_styles' ) );
44 +
45 + // Deactivation survey.
46 + add_action( 'admin_footer-plugins.php', array( $this, 'load_deactivation_survey_modal' ) );
42 47 }
43 48
44 49 /**
45 50 * Add global styles.
@@ -46,9 +51,28 @@
46 51 */
47 52 public function add_global_styles() {
48 53 echo '<style>
49 54 #toplevel_page_wp-defender ul.wp-submenu li a[href="admin.php?page=wdf-ip-lockout"] { display: flex; justify-content: space-between; align-items: center; }
55 + #adminmenu li.wp-has-current-submenu a.wp-has-current-submenu { background-color: #4763E4 !important; }
56 + #adminmenu .toplevel_page_wp-defender div.wp-menu-image.svg { width: 16px; height: 16px; margin-top: 10px; margin-left: 10px; background-size: 16px auto; }
50 57 </style>';
58 + if ( ! $this->is_wp_org_version ) {
59 + echo '<style>
60 + #adminmenu .defender-admin-menu-pro-tag {
61 + display: inline-block;
62 + padding: 0;
63 + color: inherit;
64 + border: 1px solid currentColor;
65 + border-radius: 9px;
66 + line-height: 16px;
67 + font-size: 11px;
68 + height: 16px;
69 + width: 28px;
70 + text-align: center;
71 + margin-left: 5px;
72 + }
73 + </style>';
74 + }
51 75 }
52 76
53 77 /**
54 78 * Init admin actions.
@@ -59,9 +83,8 @@
59 83 add_filter( 'plugin_action_links_' . DEFENDER_PLUGIN_BASENAME, array( $this, 'settings_link' ) );
60 84 add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 3 );
61 85 // Only for plugin pages and actions are only for wp.org members.
62 86 if ( $this->is_wp_org_version ) {
63 - add_action( 'admin_init', array( $this, 'register_free_modules' ), 20 );
64 87 /**
65 88 * Action hook that fires after a scan issue is fixed.
66 89 *
67 90 * @since 4.4.0
@@ -73,15 +96,25 @@
73 96 );
74 97 // For submenu callout.
75 98 add_action( 'admin_head', array( $this, 'retarget_submenu_callout' ) );
76 99 if ( ! wd_di()->get( WPMUDEV::class )->is_wpmu_hosting() ) {
100 + $upsell_menu_title = sprintf(
101 + '<span class="defender-upsell-label">%1$s<span class="defender-upsell-pro-tag">%2$s</span></span><svg class="defender-upsell-arrow" width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0.225625 8.7625C0.0881251 8.62917 0.013125 8.47083 0.000624999 8.2875C-0.00770833 8.10417 0.0672918 7.93125 0.225625 7.76875L5.86313 2.125L7.16313 0.93125C7.31313 0.789584 7.47146 0.722917 7.63812 0.73125C7.80479 0.739583 7.94854 0.804167 8.06938 0.925C8.19021 1.04583 8.25062 1.18958 8.25062 1.35625C8.25479 1.52292 8.18812 1.67708 8.05062 1.81875L6.85063 3.11875L1.21313 8.75C1.05896 8.90417 0.890209 8.97917 0.706875 8.975C0.527709 8.975 0.367292 8.90417 0.225625 8.7625ZM7.61938 4.4625L7.76312 1.24375L4.43188 1.3625H2.30687C2.11521 1.3625 1.94854 1.29792 1.80688 1.16875C1.66521 1.03542 1.59438 0.875 1.59438 0.6875C1.59438 0.5 1.66104 0.339584 1.79438 0.20625C1.93188 0.0687502 2.10688 0 2.31938 0H8.25688C8.48188 0 8.66104 0.0687502 8.79437 0.20625C8.92771 0.34375 8.99438 0.520834 8.99438 0.7375V6.66875C8.99438 6.87292 8.92563 7.04583 8.78813 7.1875C8.65063 7.325 8.48812 7.39375 8.30062 7.39375C8.10896 7.39375 7.94646 7.325 7.81313 7.1875C7.68396 7.04583 7.61938 6.87708 7.61938 6.68125V4.4625Z" fill="white"/></svg>',
102 + esc_html__( 'Upgrade', 'defender-security' ),
103 + esc_html__( 'Pro', 'defender-security' )
104 + );
77 105 add_submenu_page(
78 106 'wp-defender',
79 - esc_html__( 'Limited-time offer!', 'defender-security' ),
80 - esc_html__( 'Limited-time offer!', 'defender-security' ),
107 + esc_html__( 'Upgrade to Pro', 'defender-security' ),
108 + $upsell_menu_title,
81 109 is_multisite() ? 'manage_network_options' : 'manage_options',
82 - $this->get_link( 'upsell', 'defender_submenu_upsell' )
110 + $this->get_link( 'upsell', 'defender_new-submenu_upsell' )
83 111 );
112 + global $submenu;
113 + if ( isset( $submenu['wp-defender'] ) && is_array( $submenu['wp-defender'] ) && array() !== $submenu['wp-defender'] ) {
114 + $last = array_key_last( $submenu['wp-defender'] );
115 + $submenu['wp-defender'][ $last ][4] = 'defender-menu-upsell'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
116 + }
84 117 }
85 118 }
86 119
87 120 // Display IP detection notice.
@@ -92,8 +125,30 @@
92 125 }
93 126 }
94 127
95 128 /**
129 + * Initialize the deactivation survey modal.
130 + */
131 + public function init_deactivation_survey() {
132 + global $pagenow;
133 +
134 + if ( 'plugins.php' !== $pagenow ) {
135 + return;
136 + }
137 +
138 + wp_enqueue_style( 'def-sui' );
139 + wp_enqueue_style( 'def-admin' );
140 +
141 + if ( ! wp_script_is( 'clipboard', 'enqueued' ) ) {
142 + wp_enqueue_script( 'clipboard' );
143 + }
144 +
145 + wp_enqueue_script( 'wpmudev-sui' );
146 + wp_enqueue_script( 'def-deactivation-survey' );
147 + wp_enqueue_script( 'def-admin' );
148 + }
149 +
150 + /**
96 151 * Retrieves the display name of the plugin.
97 152 *
98 153 * @return string The display name of the plugin with a trailing hyphen.
99 154 */
@@ -108,9 +163,9 @@
108 163
109 164 // If whitelabeling is enabled and a custom name is provided, use it.
110 165 if ( $whitelabel->can_whitelabel() ) {
111 166 $custom_label = $whitelabel->get_plugin_name( Config_Hub_Helper::WDP_ID );
112 - if ( ! empty( $custom_label ) ) {
167 + if ( is_string( $custom_label ) && '' !== trim( $custom_label ) ) {
113 168 $plugin_label = $custom_label;
114 169 }
115 170 }
116 171
@@ -118,14 +173,8 @@
118 173 return $plugin_label . ' - ';
119 174 }
120 175
121 176 /**
122 - * The method is a stub without content.
123 - */
124 - private function menu_nope(): void {
125 - }
126 -
127 - /**
128 177 * Generates the submenu callout for the WP Defender plugin.
129 178 *
130 179 * @return void
131 180 */
@@ -131,27 +180,62 @@
131 180 */
132 181 public function retarget_submenu_callout(): void {
133 182 ?>
134 183 <style>
135 - #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"],
136 - #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:hover,
137 - #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:active,
138 - #toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]:focus {
139 - background: #8D00B1;
184 + #adminmenu .wp-submenu li.defender-menu-upsell > a,
185 + #adminmenu .wp-submenu li.defender-menu-upsell > a:hover,
186 + #adminmenu .wp-submenu li.defender-menu-upsell > a:active,
187 + #adminmenu .wp-submenu li.defender-menu-upsell > a:focus {
188 + display: flex;
189 + align-items: center;
190 + justify-content: space-between;
191 + height: 32px;
192 + padding: 7px 12px;
193 + box-sizing: border-box;
194 + background: #571EE7;
140 195 color: #ffffff;
141 - font-weight: 500;
196 + font-size: 14px;
197 + font-weight: 400;
198 + line-height: 18px;
142 199 }
143 200
144 - #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"],
145 - #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:hover,
146 - #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:active,
147 - #toplevel_page_wp-defender.wp-not-current-submenu > ul > li:last-child > a[href^="https://wpmudev.com/"]:focus {
201 + #adminmenu .wp-submenu li.defender-menu-upsell > a .defender-upsell-label {
202 + display: inline-flex;
203 + align-items: center;
204 + }
205 +
206 + #adminmenu .wp-submenu li.defender-menu-upsell > a .defender-upsell-pro-tag {
207 + display: inline-flex;
208 + align-items: center;
209 + justify-content: center;
210 + margin-left: 6px;
211 + padding: 0 5px;
212 + border: 1px solid #ffffff;
213 + border-radius: 9px;
214 + width: 30px;
215 + height: 18px;
216 + box-sizing: border-box;
217 + font-family: 'Roboto', Arial, sans-serif;
218 + font-size: 12px;
219 + font-weight: 400;
220 + line-height: 20px;
221 + letter-spacing: 0.1px;
222 + }
223 +
224 + #adminmenu .wp-submenu li.defender-menu-upsell > a .defender-upsell-arrow {
225 + margin-right: 4px;
226 + }
227 +
228 + #toplevel_page_wp-defender.wp-not-current-submenu .wp-submenu li.defender-menu-upsell > a,
229 + #toplevel_page_wp-defender.wp-not-current-submenu .wp-submenu li.defender-menu-upsell > a:hover,
230 + #toplevel_page_wp-defender.wp-not-current-submenu .wp-submenu li.defender-menu-upsell > a:active,
231 + #toplevel_page_wp-defender.wp-not-current-submenu .wp-submenu li.defender-menu-upsell > a:focus {
148 232 margin-left: -4px;
149 233 }
150 234 </style>
151 235 <script type='text/javascript'>
152 236 jQuery(function ($) {
153 - $('#toplevel_page_wp-defender > ul > li:last-child > a[href^="https://wpmudev.com/"]').attr("target", "_blank");
237 + $('#adminmenu li.defender-menu-upsell > a').attr("target", "_blank");
154 238 });
155 239 </script>
156 240 <?php
157 241 }
@@ -170,15 +254,16 @@
170 254 *
171 255 * @param string $link_for Accepts: 'docs', 'plugin', 'rate' and etc.
172 256 * @param string $campaign Utm campaign tag to be used in link. Default: ''.
173 257 * @param string $adv_path Advanced path. Default: ''.
258 + * @param string $source UTM source. Default: 'defender'.
174 259 *
175 260 * @return string
176 261 */
177 - public function get_link( $link_for, $campaign = '', $adv_path = '' ): string {
262 + public function get_link( $link_for, $campaign = '', $adv_path = '', $source = 'defender' ): string {
178 263 $domain = 'https://wpmudev.com';
179 264 $wp_org = 'https://wordpress.org';
180 - $utm_tags = "?utm_source=defender&utm_medium=plugin&utm_campaign={$campaign}";
265 + $utm_tags = "?utm_source={$source}&utm_medium=plugin&utm_campaign={$campaign}";
181 266 switch ( $link_for ) {
182 267 case 'docs':
183 268 $link = "{$domain}/docs/wpmu-dev-plugins/defender/{$utm_tags}";
184 269 break;
@@ -193,8 +278,11 @@
193 278 $link = $this->is_wp_org_version
194 279 ? "{$wp_org}/support/plugin/defender-security/"
195 280 : "{$domain}/get-support/";
196 281 break;
282 + case 'support_with_utm':
283 + $link = "{$domain}/hub2/support/{$utm_tags}";
284 + break;
197 285 case 'roadmap':
198 286 $link = "{$domain}/roadmap/";
199 287 break;
200 288 case 'pro_link':
@@ -245,9 +333,9 @@
245 333 esc_html__(
246 334 'Upgrade to Defender Pro',
247 335 'defender-security'
248 336 )
249 - ) . '">' . esc_html__( 'Limited-time offer!', 'defender-security' ) . '</a>';
337 + ) . '">' . esc_html__( 'Get Defender Pro', 'defender-security' ) . '</a>';
250 338 }
251 339 } elseif ( ! $wpmu_dev->is_hosted_site_connected_to_tfh() ) {
252 340 $action_links['renew'] = '<a style="color: #8D00B1;" target="_blank" href="' . $this->get_link(
253 341 'plugin',
@@ -344,34 +432,16 @@
344 432 return array_merge( $links, $row_meta );
345 433 }
346 434
347 435 /**
348 - * Register sub-modules.
349 - */
350 - public function register_free_modules() {
351 - $module_path = defender_path( 'extra/free-dashboard/module.php' );
352 - if ( ! file_exists( $module_path ) ) {
353 - return;
354 - }
355 - /* @noinspection PhpIncludeInspection */
356 - require_once $module_path;
357 - // Register the current plugin.
358 - do_action(
359 - 'wdev_register_plugin',
360 - /* 1 Plugin ID */ DEFENDER_PLUGIN_BASENAME,
361 - /* 2 Plugin Title */ 'Defender',
362 - /* 3 https://wordpress.org */ '/plugins/defender-security/',
363 - /* 4 Email Button CTA */ esc_html__( 'Get Fast!', 'defender-security' )
364 - );
365 - }
366 -
367 - /**
368 - * Display IP detection notices for if user site is behind proxy, e.g. Cloudflare or something else, and only for admins.
436 + * Display IP detection notices:
437 + * - if user site is behind proxy, e.g. Cloudflare or something else, and only for admins,
438 + * - only on the plugin's pages.
369 439 *
370 440 * @return void
371 441 */
372 442 public function admin_notices(): void {
373 - if ( ! current_user_can( 'manage_options' ) ) {
443 + if ( ! current_user_can( 'manage_options' ) || ! is_defender_page() ) {
374 444 return;
375 445 }
376 446 $header = $this->get_plugin_display_name();
377 447 if ( Firewall::is_cf_notice_ready() ) {
@@ -500,9 +570,9 @@
500 570 );
501 571 }
502 572
503 573 $prop = defender_get_data_from_request( 'prop', 'p' );
504 - $notice_type = ! empty( $prop ) ? $prop : false;
574 + $notice_type = '' !== trim( $prop ) ? $prop : false;
505 575 if ( 'notice-for-cf' === $notice_type ) {
506 576 update_site_option( Firewall::IP_DETECTION_CF_DISMISS_SLUG, true );
507 577 wp_send_json_success();
508 578 } elseif ( 'notice-for-xff' === $notice_type ) {
@@ -532,19 +602,100 @@
532 602 // Change model's data.
533 603 $model_firewall = wd_di()->get( Model\Setting\Firewall::class );
534 604 $model_firewall->http_ip_header = 'HTTP_X_FORWARDED_FOR';
535 605 $xff_ip = defender_get_data_from_request( 'HTTP_X_FORWARDED_FOR', 's' );
536 - if ( empty( $model_firewall->trusted_proxies_ip ) ) {
606 + $xff_parts = preg_split( '/\s*,\s*/', $xff_ip );
607 + $xff_parts = is_array( $xff_parts ) ? $xff_parts : array();
608 + $xff_parts = array_map( 'trim', $xff_parts );
609 + $xff_parts = array_filter(
610 + $xff_parts,
611 + static function ( $ip ) {
612 + return false !== filter_var( $ip, FILTER_VALIDATE_IP );
613 + }
614 + );
615 + $separator = "\r\n";
616 + $xff_parts = array_unique( $xff_parts );
617 + $xff_ip = implode( $separator, $xff_parts );
618 + if ( '' === $xff_ip ) {
619 + wp_send_json_error(
620 + array( 'message' => esc_html__( 'Invalid trusted proxy IP(s) detected in X-Forwarded-For header.', 'defender-security' ) )
621 + );
622 + }
623 + if ( '' === $model_firewall->trusted_proxies_ip ) {
537 624 $model_firewall->trusted_proxies_ip = $xff_ip;
538 625 } else {
539 626 // Todo: improve the code using a separate method. This will be useful when the user switches between different proxy headeres (IP detection options).
540 - $separator = "\r\n";
541 - // Check if the XFF header contains multiple IPs.
542 - $xff_ip = str_replace( array( ',', ' ,' ), $separator, $xff_ip );
543 627 $model_firewall->trusted_proxies_ip = $model_firewall->trusted_proxies_ip . $separator . $xff_ip;
544 628 }
545 629 $model_firewall->save();
546 630 // Save Dismiss slug.
547 631 update_site_option( Firewall::IP_DETECTION_XFF_DISMISS_SLUG, true );
632 + wp_send_json_success();
633 + }
634 +
635 + /**
636 + * Load deactivation survey modal.
637 + */
638 + public function load_deactivation_survey_modal() {
639 + $deactivation_survey_template_file = WP_DEFENDER_DIR .
640 + 'src' . DIRECTORY_SEPARATOR .
641 + 'view' . DIRECTORY_SEPARATOR .
642 + 'modal' . DIRECTORY_SEPARATOR .
643 + 'deactivation-survey.php';
644 +
645 + if ( ! file_exists( $deactivation_survey_template_file ) ) {
646 + return;
647 + }
648 +
649 + // Data to be passed to the template file.
650 + $is_pro = wd_di()->get( WPMUDEV::class )->is_pro();
651 + $docs_link = $this->get_link(
652 + 'support_with_utm',
653 + 'defender_deactivation_survey_help',
654 + '',
655 + $is_pro ? 'defender-pro' : 'defender'
656 + );
657 +
658 + ob_start();
659 + require_once $deactivation_survey_template_file;
660 + // Everything escaped in all template files.
661 + echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
662 + }
663 +
664 + /**
665 + * Track deactivation.
666 + */
667 + public function track_deactivate() {
668 + if (
669 + ! current_user_can( 'manage_options' ) ||
670 + ! check_ajax_referer( 'defender_deactivation_survey_modal' )
671 + ) {
672 + wp_send_json_error(
673 + array( 'message' => esc_html__( 'Invalid request, you are not allowed to do that action.', 'defender-security' ) )
674 + );
675 + }
676 + $posted_data = defender_get_data_from_request( null, 'p' );
677 + if ( ! is_array( $posted_data['properties'] ) ) {
678 + wp_send_json_error(
679 + array( 'message' => esc_html__( 'Invalid request, allowed data not provided.', 'defender-security' ) )
680 + );
681 + }
682 + $properties = $posted_data['properties'];
683 + if (
684 + ! isset(
685 + $properties['Reason'],
686 + $properties['Message'],
687 + $properties['Modal Action'],
688 + $properties['Requested Assistance'],
689 + $properties['Tracking Status']
690 + )
691 + ) {
692 + wp_send_json_error(
693 + array( 'message' => esc_html__( 'Missing field(s).', 'defender-security' ) )
694 + );
695 + }
696 +
697 + wd_di()->get( Deactivation_Survey::class )->track_deactivation_survey( $properties );
698 +
548 699 wp_send_json_success();
549 700 }
550 701 }