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

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

324 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'] )
142 && $object->configs['error_send']
143 && in_array( $scan->status, array( Scan::STATUS_ERROR, Scan::STATUS_IDLE ), true )
144 ) {
145 $subject = $object->configs['template']['error']['subject'];
146 $content = nl2br( $object->configs['template']['error']['body'] );
147
148 $this->email_engine( $scan, $object, $subject, $content, $issues );
149
150 return;
151 }
152
153 // Sometimes value may be as empty string for disabled 'always_send'.
154 if (
155 ( ! isset( $object->configs['always_send'] ) || true !== $object->configs['always_send'] )
156 && 0 === count( $issues )
157 ) {
158 return;
159 }
160 // For another case.
161 $templates = count( $issues ) ? $object->configs['template']['found'] : $object->configs['template']['not_found'];
162 $subject = $templates['subject'];
163 $content = nl2br( $templates['body'] );
164
165 $this->email_engine( $scan, $object, $subject, $content, $issues );
166 }
167
168 /**
169 * Sends the malware notification email to a specific user.
170 * Constructs and sends the malware notification email to the specified recipient.
171 *
172 * @param string $email The recipient's email address.
173 * @param string $name The recipient's name.
174 * @param string $subject The email subject.
175 * @param string $content The email content.
176 * @param array $issues The malware issues detected.
177 * @param object $service The notification service object.
178 *
179 * @return void
180 */
181 private function send_to_user( $email, $name, $subject, $content, $issues, $service ) {
182 $wpmudev = new WPMUDEV();
183 $maybe_show_upsell = ! $wpmudev->is_whitelabel_enabled() && $wpmudev->is_pro();
184 $controller_scan = wd_di()->get( \WP_Defender\Controller\Scan::class );
185 $replacers = array(
186 'issues_count' => is_array( $issues ) || $issues instanceof \Countable ? (string) count( $issues ) : '0',
187 'issues_list' => $controller_scan->render_partial(
188 'email/scan-issue-list',
189 array(
190 'issues' => $issues,
191 'email' => $email,
192 ),
193 false
194 ),
195 'site_url' => network_site_url(),
196 'user_name' => $name,
197 );
198
199 /**
200 * Filters the replaced data. It's without replacement.
201 *
202 * @param array $replacers Email params.
203 *
204 * @deprecated 3.2.0
205 */
206 $replacers = apply_filters_deprecated( 'wd_notification_email_params', array( $replacers ), '3.2.0' );
207
208 $filter_name = ( is_array( $issues ) || $issues instanceof Countable ? count( $issues ) : 0 ) > 0
209 ? 'wd_notification_email_subject_issue'
210 : 'wd_notification_email_subject';
211 $subject = apply_filters( $filter_name, $subject );
212
213 /**
214 * It's without replacement.
215 *
216 * @deprecated 3.2.0
217 */
218 $content = apply_filters_deprecated(
219 'wd_notification_email_content_before',
220 array( $content ),
221 '3.2.0'
222 );
223 foreach ( $replacers as $key => $replacer ) {
224
225 if ( is_scalar( $replacer ) && ! is_string( $replacer ) ) {
226 $replacer = (string) $replacer;
227 }
228
229 $content = str_replace( "{{$key}}", $replacer, $content );
230 $content = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $content );
231 $subject = str_replace( "{{$key}}", $replacer, $subject );
232 $subject = str_replace( '{' . strtoupper( $key ) . '}', $replacer, $subject );
233 }
234 /**
235 * It's without replacement.
236 *
237 * @deprecated 3.2.0
238 */
239 $content = apply_filters_deprecated(
240 'wd_notification_email_content_after',
241 array( $content ),
242 '3.2.0'
243 );
244 $unsubscribe_link = $service->create_unsubscribe_url( $this->slug, $email );
245 $template = $controller_scan->render_partial(
246 'email/index',
247 array(
248 'title' => esc_html__( 'Malware Scanning', 'defender-security' ),
249 'content_body' => sprintf( '<div style="font-size: 16px">%s</div>', $content ),
250 'unsubscribe_link' => $unsubscribe_link,
251 ),
252 false
253 );
254
255 $headers = wd_di()->get( Mail::class )->get_headers(
256 defender_noreply_email( 'wd_scan_noreply_email' ),
257 self::SLUG
258 );
259
260 $ret = wp_mail( $email, $subject, $template, $headers );
261 if ( $ret ) {
262 $this->save_log( $email );
263 }
264 }
265
266 /**
267 * Defines labels for the malware notification settings.
268 *
269 * @return array The array of malware notification settings labels.
270 */
271 public function labels(): array {
272 return array(
273 'notification' => esc_html__( 'Malware Scanning - Notification', 'defender-security' ),
274 'always_send_notification' => esc_html__( 'Send notifications when no issues are detected', 'defender-security' ),
275 'error_send' => esc_html__(
276 "Send notifications when Defender couldn't scan the files",
277 'defender-security'
278 ),
279 'email_subject_issue_found' => esc_html__( 'Email subject when an issue is found', 'defender-security' ),
280 'email_subject_issue_not_found' => esc_html__( 'Email subject when no issues are found', 'defender-security' ),
281 'email_subject_error' => esc_html__( 'Email subject when failed to scan', 'defender-security' ),
282 'email_content_issue_found' => esc_html__( 'Email body when an issue is found', 'defender-security' ),
283 'email_content_issue_not_found' => esc_html__( 'Email body when no issues are found', 'defender-security' ),
284 'email_content_error' => esc_html__( 'When failed to scan', 'defender-security' ),
285 'notification_subscribers' => esc_html__( 'Recipients', 'defender-security' ),
286 );
287 }
288
289 /**
290 * Checks if the malware scanning notification options are enabled.
291 * Checks whether the malware scanning notification is enabled or if the malware reporting is enabled or the scan
292 * is automated.
293 *
294 * @return bool True if the malware scanning notification options are enabled, false otherwise.
295 */
296 public function check_options(): bool {
297 return self::STATUS_ACTIVE === $this->status
298 || self::STATUS_ACTIVE === wd_di()->get( Malware_Report::class )->status;
299 }
300
301 /**
302 * Additional converting rules.
303 *
304 * @param array $configs The configuration data.
305 *
306 * @return array The type-casted configuration data.
307 * @since 3.1.0
308 */
309 public function type_casting( array $configs ): array {
310 $configs['always_send'] = (bool) $configs['always_send'];
311 $configs['error_send'] = (bool) $configs['error_send'];
312 // Additional security layer because parent 'configs'-property doesn't have sanitized stuff for nested properties.
313 $template = $configs['template'];
314 $configs['template']['found']['subject'] = sanitize_text_field( $template['found']['subject'] );
315 $configs['template']['found']['body'] = sanitize_textarea_field( $template['found']['body'] );
316 $configs['template']['not_found']['subject'] = sanitize_text_field( $template['not_found']['subject'] );
317 $configs['template']['not_found']['body'] = sanitize_textarea_field( $template['not_found']['body'] );
318 $configs['template']['error']['subject'] = sanitize_text_field( $template['error']['subject'] );
319 $configs['template']['error']['body'] = sanitize_textarea_field( $template['error']['body'] );
320
321 return $configs;
322 }
323 }
324