PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.6.0
Defender Security – Malware Scanner, Login Security & Firewall v5.6.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 / model / notification / class-malware-notification.php

class-malware-notification.php in Defender Security – Malware Scanner, Login Security & Firewall 5.6.0, at src/model/notification/class-malware-notification.php

322 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles the scheduling and sending of malware-related notifications.
4 *
5 * @package WP_Defender\Model\Notification
6 */
7
8 namespace WP_Defender\Model\Notification;
9
10 use Countable;
11 use WP_Defender\Traits\IO;
12 use WP_Defender\Model\Scan;
13 use WP_Defender\Component\Mail;
14 use WP_Defender\Component\Notification;
15 use WP_Defender\Model\Scan_Item;
16 use WP_Defender\Model\Notification\Malware_Report;
17 use WP_Defender\Traits\Scan_Email_Template;
18 use WP_Defender\Traits\Scan_Upsell;
19 use WP_Defender\Behavior\WPMUDEV;
20
21 /**
22 * Handles the scheduling and sending of malware-related notifications.
23 */
24 class Malware_Notification extends \WP_Defender\Model\Notification {
25
26 use IO;
27 use Scan_Email_Template;
28 use Scan_Upsell;
29
30 /**
31 * Table name.
32 *
33 * @var string
34 */
35 protected $table = 'wd_malware_scanning_notification';
36 /**
37 * Slug identifier for the malware notification.
38 *
39 * @var string
40 */
41 public const SLUG = 'malware-notification';
42
43 /**
44 * Retrieves the default values for the malware notification.
45 *
46 * @return array The default values for the malware notification.
47 */
48 public function get_default_values(): array {
49 $template = $this->get_email_template();
50
51 return array(
52 'subject_issue_found' => $template['found']['subject'],
53 'subject_issue_not_found' => $template['not_found']['subject'],
54 'subject_error' => $template['error']['subject'],
55 'content_issue_found' => $template['found']['body'],
56 'content_issue_not_found' => $template['not_found']['body'],
57 'content_error' => $template['error']['body'],
58 );
59 }
60
61 /**
62 * Sets default values for the malware notification.
63 *
64 * @return void
65 */
66 protected function before_load(): void {
67 $default = array(
68 'slug' => self::SLUG,
69 'title' => esc_html__( 'Malware Scanning - Notification', 'defender-security' ),
70 'status' => self::STATUS_DISABLED,
71 'description' => esc_html__(
72 'Get email notifications when Defender has finished manual malware scans.',
73 'defender-security'
74 ),
75 // @since 3.0.0 Fix 'Guest'-line.
76 'in_house_recipients' => is_user_logged_in() ? array( $this->get_default_user() ) : array(),
77 'out_house_recipients' => array(),
78 'type' => 'notification',
79 'dry_run' => false,
80 'configs' => array(
81 'always_send' => false,
82 'error_send' => false,
83 'template' => $this->get_email_template(),
84 ),
85 );
86 $this->import( $default );
87 }
88
89 /**
90 * Sends the malware notification email to all subscribed users.
91 *
92 * @param Scan $scan The scan object.
93 * @param object $recipient_list The object containing the recipient lists.
94 * @param string $subject The subject of the email.
95 * @param string $content The content of the email.
96 * @param array $issues The list of issues to include in the email.
97 */
98 protected function email_engine( Scan $scan, $recipient_list, $subject, $content, $issues ): void {
99 $service = wd_di()->get( Notification::class );
100 foreach ( $recipient_list->in_house_recipients as $user ) {
101 if ( self::USER_SUBSCRIBED !== $user['status'] ) {
102 continue;
103 }
104 $this->send_to_user( $user['email'], $user['name'], $subject, $content, $issues, $service );
105 }
106 foreach ( $recipient_list->out_house_recipients as $user ) {
107 if ( self::USER_SUBSCRIBED !== $user['status'] ) {
108 continue;
109 }
110 $this->send_to_user( $user['email'], $user['name'], $subject, $content, $issues, $service );
111 }
112 }
113
114 /**
115 * Sends the malware notification email.
116 * Constructs and sends the malware notification email to the recipients.
117 *
118 * @param Scan $scan The scan object.
119 *
120 * @return void
121 */
122 public function send( Scan $scan ): void {
123 $object = $this;
124 if ( $scan->is_automation ) {
125 $object = wd_di()->get( Malware_Report::class );
126 // If Malware Reporting is unchecked, then return.
127 if ( self::STATUS_ACTIVE !== $object->status ) {
128 return;
129 }
130 // @since 2.7.0 Remove 'dry_run'-condition.
131 }
132 $issues = $scan->get_issues( null, Scan_Item::STATUS_ACTIVE );
133 /**
134 * When the Defender is unable to scan files for some reason and the 'error_send' option is checked.
135 * Possible reasons:
136 * - no connection to Scan API,
137 * - no checksums received from wp.org,
138 * - scan process stuck.
139 */
140 if (
141 isset( $object->configs['error_send'] ) && ! empty(
142 $object->configs['error_send']
143 && in_array( $scan->status, array( Scan::STATUS_ERROR, Scan::STATUS_IDLE ), true )
144 )
145 ) {
146 $subject = $object->configs['template']['error']['subject'];
147 $content = nl2br( $object->configs['template']['error']['body'] );
148
149 $this->email_engine( $scan, $object, $subject, $content, $issues );
150
151 return;
152 }
153
154 // Sometimes value may be as empty string for disabled 'always_send'.
155 if ( empty( $object->configs['always_send'] ) && 0 === count( $issues ) ) {
156 return;
157 }
158 // For another case.
159 $templates = count( $issues ) ? $object->configs['template']['found'] : $object->configs['template']['not_found'];
160 $subject = $templates['subject'];
161 $content = nl2br( $templates['body'] );
162
163 $this->email_engine( $scan, $object, $subject, $content, $issues );
164 }
165
166 /**
167 * Sends the malware notification email to a specific user.
168 * Constructs and sends the malware notification email to the specified recipient.
169 *
170 * @param string $email The recipient's email address.
171 * @param string $name The recipient's name.
172 * @param string $subject The email subject.
173 * @param string $content The email content.
174 * @param array $issues The malware issues detected.
175 * @param object $service The notification service object.
176 *
177 * @return void
178 */
179 private function send_to_user( $email, $name, $subject, $content, $issues, $service ) {
180 $wpmudev = new WPMUDEV();
181 $maybe_show_upsell = ! $wpmudev->is_whitelabel_enabled() && $wpmudev->is_pro();
182 $controller_scan = wd_di()->get( \WP_Defender\Controller\Scan::class );
183 $replacers = array(
184 'issues_count' => is_array( $issues ) || $issues instanceof \Countable ? (string) count( $issues ) : '0',
185 'issues_list' => $controller_scan->render_partial(
186 'email/scan-issue-list',
187 array(
188 'issues' => $issues,
189 'email' => $email,
190 ),
191 false
192 ),
193 'site_url' => network_site_url(),
194 'user_name' => $name,
195 );
196
197 /**
198 * Filters the replaced data. It's without replacement.
199 *
200 * @param array $replacers Email params.
201 *
202 * @deprecated 3.2.0
203 */
204 $replacers = apply_filters_deprecated( 'wd_notification_email_params', array( $replacers ), '3.2.0' );
205
206 $filter_name = ( is_array( $issues ) || $issues instanceof Countable ? count( $issues ) : 0 ) > 0
207 ? 'wd_notification_email_subject_issue'
208 : 'wd_notification_email_subject';
209 $subject = apply_filters( $filter_name, $subject );
210
211 /**
212 * It's without replacement.
213 *
214 * @deprecated 3.2.0
215 */
216 $content = apply_filters_deprecated(
217 'wd_notification_email_content_before',
218 array( $content ),
219 '3.2.0'
220 );
221 foreach ( $replacers as $key => $replacer ) {
222
223 if ( is_scalar( $replacer ) && ! is_string( $replacer ) ) {
224 $replacer = (string) $replacer;
225 }
226
227 $content = str_replace( "{{$key}}", $replacer, $content );
228 $content = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $content );
229 $subject = str_replace( "{{$key}}", $replacer, $subject );
230 $subject = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $subject );
231 }
232 /**
233 * It's without replacement.
234 *
235 * @deprecated 3.2.0
236 */
237 $content = apply_filters_deprecated(
238 'wd_notification_email_content_after',
239 array( $content ),
240 '3.2.0'
241 );
242 $unsubscribe_link = $service->create_unsubscribe_url( $this->slug, $email );
243 $template = $controller_scan->render_partial(
244 'email/index',
245 array(
246 'title' => esc_html__( 'Malware Scanning', 'defender-security' ),
247 'content_body' => sprintf( '<div style="font-size: 16px">%s</div>', $content ),
248 'unsubscribe_link' => $unsubscribe_link,
249 ),
250 false
251 );
252
253 $headers = wd_di()->get( Mail::class )->get_headers(
254 defender_noreply_email( 'wd_scan_noreply_email' ),
255 self::SLUG
256 );
257
258 $ret = wp_mail( $email, $subject, $template, $headers );
259 if ( $ret ) {
260 $this->save_log( $email );
261 }
262 }
263
264 /**
265 * Defines labels for the malware notification settings.
266 *
267 * @return array The array of malware notification settings labels.
268 */
269 public function labels(): array {
270 return array(
271 'notification' => esc_html__( 'Malware Scanning - Notification', 'defender-security' ),
272 'always_send_notification' => esc_html__( 'Send notifications when no issues are detected', 'defender-security' ),
273 'error_send' => esc_html__(
274 "Send notifications when Defender couldn't scan the files",
275 'defender-security'
276 ),
277 'email_subject_issue_found' => esc_html__( 'Email subject when an issue is found', 'defender-security' ),
278 'email_subject_issue_not_found' => esc_html__( 'Email subject when no issues are found', 'defender-security' ),
279 'email_subject_error' => esc_html__( 'Email subject when failed to scan', 'defender-security' ),
280 'email_content_issue_found' => esc_html__( 'Email body when an issue is found', 'defender-security' ),
281 'email_content_issue_not_found' => esc_html__( 'Email body when no issues are found', 'defender-security' ),
282 'email_content_error' => esc_html__( 'When failed to scan', 'defender-security' ),
283 'notification_subscribers' => esc_html__( 'Recipients', 'defender-security' ),
284 );
285 }
286
287 /**
288 * Checks if the malware scanning notification options are enabled.
289 * Checks whether the malware scanning notification is enabled or if the malware reporting is enabled or the scan
290 * is automated.
291 *
292 * @return bool True if the malware scanning notification options are enabled, false otherwise.
293 */
294 public function check_options(): bool {
295 return self::STATUS_ACTIVE === $this->status
296 || self::STATUS_ACTIVE === wd_di()->get( Malware_Report::class )->status;
297 }
298
299 /**
300 * Additional converting rules.
301 *
302 * @param array $configs The configuration data.
303 *
304 * @return array The type-casted configuration data.
305 * @since 3.1.0
306 */
307 public function type_casting( array $configs ): array {
308 $configs['always_send'] = (bool) $configs['always_send'];
309 $configs['error_send'] = (bool) $configs['error_send'];
310 // Additional security layer because parent 'configs'-property doesn't have sanitized stuff for nested properties.
311 $template = $configs['template'];
312 $configs['template']['found']['subject'] = sanitize_text_field( $template['found']['subject'] );
313 $configs['template']['found']['body'] = sanitize_textarea_field( $template['found']['body'] );
314 $configs['template']['not_found']['subject'] = sanitize_text_field( $template['not_found']['subject'] );
315 $configs['template']['not_found']['body'] = sanitize_textarea_field( $template['not_found']['body'] );
316 $configs['template']['error']['subject'] = sanitize_text_field( $template['error']['subject'] );
317 $configs['template']['error']['body'] = sanitize_textarea_field( $template['error']['body'] );
318
319 return $configs;
320 }
321 }
322