PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.3.1
Defender Security – Malware Scanner, Login Security & Firewall v5.3.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.3.1, at src/model/notification/class-malware-notification.php

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