'; // to reduce code smells private $powered_by_url = 'https://patchstack.com'; private $vmPageSlug = 'admin.php?page=onecom-wp-health-monitor#vm-settings'; private $vm_list = 'admin.php?page=onecom-wp-health-monitor#vm-page'; private $list_checkmark = 'https://wpaddon-static.group-cdn.one/images/wp/onecom/green-checkmark-16x16.png'; private $support_mail_filter_api = MIDDLEWARE_URL . '/spam/filter-support-emails'; private $guide_links = array( 'en' => 'https://help.one.com/hc/en-us/articles/4402283373841-Roll-back-plugins-and-themes-to-a-previous-version', 'da' => 'https://help.one.com/hc/da/articles/4402283373841-Rul-plugins-og-temaer-tilbage-til-en-tidligere-version', 'de' => 'https://help.one.com/hc/de/articles/4402283373841-Zur%C3%BCcksetzen-von-Plugins-und-Themes-auf-eine-fr%C3%BChere-Version', 'es' => 'https://help.one.com/hc/es/articles/4402283373841-Revertir-plugins-y-temas-a-una-versi%C3%B3n-anterior-', 'fr' => 'https://help.one.com/hc/fr/articles/4402283373841-R%C3%A9initialisation-des-plugins-et-des-th%C3%A8mes-%C3%A0-une-version-ant%C3%A9rieure', 'fi' => 'https://help.one.com/hc/fi/articles/4402283373841-Palauta-pluginit-ja-teemat-edelliseen-versioon', 'it' => 'https://help.one.com/hc/it/articles/4402283373841-Ripristina-plugin-e-temi-alla-versione-precedente', 'nl' => 'https://help.one.com/hc/nl/articles/4402283373841-Zet-plugins-en-thema-s-terug-naar-een-vorige-versie', 'nb' => 'https://help.one.com/hc/no/articles/4402283373841-Tilbakerull-plugins-og-temaer-til-en-tidligere-versjon', 'pt' => 'https://help.one.com/hc/pt/articles/4402283373841-Reverter-plugins-e-temas-para-uma-vers%C3%A3o-anterior', 'sv' => 'https://help.one.com/hc/sv/articles/4402283373841-%C3%85terladdade-till%C3%A4gg-och-teman-till-en-f%C3%B6reg%C3%A5ende-version-', ); public function __construct() { $this->vm_page_link = is_multisite() ? get_admin_url( $this->vmPageSlug ) : admin_url( $this->vmPageSlug ); $this->vm_list_link = is_multisite() ? get_admin_url( $this->vm_list ) : admin_url( $this->vm_list ); $this->settings = new OCVMSettings(); $this->emailDir = plugin_dir_path( __DIR__ ) . 'templates/partials/'; $this->site_url = get_bloginfo( 'url' ); $this->site_url = str_replace( 'http://', '', $this->site_url ); $this->site_url = str_replace( 'https://', '', $this->site_url ); $this->site_url = str_replace( 'www.', '', $this->site_url ); } /** * Check if email for this vulnerability is already sent */ public function emailAlreadySent() { // check in DB if any mail was already sent or not. } /** * Prepare subject */ public function prepareSubject( $type ): string { $subject = sprintf( __( 'Vulnerability found on %s by one.com WordPress Vulnerability Monitor.', 'onecom-wp' ), $this->site_url ); if ( 'vulsFixed' === $type ) { $subject = sprintf( __( 'Vulnerability fixed on %s by one.com WordPress Vulnerability Monitor.', 'onecom-wp' ), $this->site_url ); } elseif ( 'vulsNotAutoFixed' === $type ) { $subject = sprintf( __( 'Vulnerability found in your WordPress installation on %s cannot be fixed automatically', 'onecom-wp' ), $this->site_url ); } return $subject; } /** * Prepare reference text */ public function prepareReference( $type ): string { $text = sprintf( __( 'Vulnerability detected in your WordPress installation on %s', 'onecom-wp' ), $this->site_url ); if ( 'vulsFixed' === $type ) { $text = sprintf( __( 'Vulnerability fixed in your WordPress installation on %s', 'onecom-wp' ), $this->site_url ); } elseif ( 'vulsNotAutoFixed' === $type ) { $text = sprintf( __( 'Vulnerability found in your WordPress installation on %s cannot be fixed automatically', 'onecom-wp' ), $this->site_url ); } return $text; } /** * Fix steps for VM emails (self-mwp cu & mwp with auto-update disable case) * @return VM emails description string (actually html) */ public function mailDescription(): string { // Default mail description (mainly used in volsFound with auto-update disable) $mail_description = __( "Note that vulnerabilities can exist both in your live website and staging environment. Please, do a manual update if you don't have automatic updates enabled in both locations.", 'onecom-wp' ); // If self-mwp customer, override description $settings = $this->settings->get(); if ( ! $this->settings->isPremium() ) { $mail_description = __( 'Note that vulnerabilities can exist both in your live website and staging environment.', 'onecom-wp' ) . ' ' . __( 'Please, do a manual update to fix the vulnerabilities.', 'onecom-wp' ); // append one time VM intro text if ( ! isset( $settings['self_mwp_intro_mail_sent'] ) || $settings['self_mwp_intro_mail_sent'] != 1 ) { $mail_description .= $this->prepareVMIntro(); // Immediately update in db that it is sent once $settings['self_mwp_intro_mail_sent'] = 1; $this->settings->update( $settings ); } } return $mail_description; } /** * Prepare VM Intro mail for self-mwp customers * This will be sent only once based on 'self_mwp_intro_mail_sent' value in db */ public function prepareVMIntro(): string { $html = '
' . __( 'What is the Vulnerability Monitor?', 'onecom-wp' ) . '
'; $img_tag = ''; $vm_setting_link = ''; $html .= "'; $html .= "
" . sprintf( __( 'You can manage email notifications in the %sVulnerability Monitor settings%s.', 'onecom-wp' ), $vm_setting_link, '' ) . '
'; return $html; } /** * Prepare disclaimer text */ public function prepareDisclaimer( $type, $items ) { $names_arr = array(); // seat belt (prepare default array) $items = array_merge( array( 'plugins' => array(), 'themes' => array(), ), $items ); foreach ( $items['plugins'] as $data ) { $names_arr[] = $data['name'] . ' v' . $data['installed_version']; } foreach ( $items['themes'] as $data ) { $names_arr[] = $data['name'] . ' v' . $data['installed_version']; } return sprintf( 'Please, disregard this email if you already updated %s to a newer version.', implode( ', ', $names_arr ) ); } /** * Prepare Email HTML body */ public function prepareEmail( $type, $items = array() ): string { if ( empty( $items ) ) { return ''; } // Get VM settings data $settings = $this->settings->get(); /** * VM emails for multiple scenarios: * * If self-mWP -> mail-vul-found-self-mwp.html * * If VM auto-updates are disabled -> mail-vul-found-auto-update-disabled.html * * If Vuls are fixed -> mail-vul-fixed.html * * If Vuls are not fixed due to no fix version -> mail-vul-found-no-fix.html */ if ( ! $this->settings->isPremium() ) { $template_path = $this->emailDir . 'mail-vul-found-self-mwp.html'; } elseif ( $settings['settings']['auto_update'] === 0 ) { $template_path = $this->emailDir . 'mail-vul-found-auto-update-disabled.html'; } elseif ( $type === 'vulsFixed' ) { $template_path = $this->emailDir . 'mail-vul-fixed.html'; } elseif ( $type === 'vulsNotAutoFixed' ) { $template_path = $this->emailDir . 'mail-vul-found-no-fix.html'; } $email_html = file_get_contents( $template_path, true ); if ( empty( $email_html ) ) { error_log( 'Could not load email html. Aborting email send task...' ); return ''; } // Common texts $site_url_inside = get_bloginfo( 'url' ); // renamed to reduce code smell $email_html = str_replace( '{{site_url}}', $site_url_inside, $email_html ); $email_html = str_replace( '{{subject}}', $this->prepareSubject( $type ), $email_html ); $email_html = str_replace( '{{reference}}', $this->prepareReference( $type ), $email_html ); $email_html = str_replace( '{{action_title}}', __( 'Action required', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{hi}}', __( 'Hi,', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{reference_text}}', __( 'Reference', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{have_a_question}}', __( "Have a question? We're here to help.", 'onecom-wp' ), $email_html ); $powered_by_link = ''; $email_html = str_replace( '{{powered_by}}', sprintf( __( 'Vulnerability monitor powered by %sPatchstack%s', 'onecom-wp' ), $powered_by_link, '' ), $email_html ); $email_html = str_replace( '{{contact_us}}', __( 'Contact us today', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{business_terms}}', __( 'Terms', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{privacy_policy}}', __( 'Privacy policy', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{unsubscribe_text}}', __( 'Unsubscribe', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{view_site_cta}}', __( 'View your site', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{view_site_link}}', home_url(), $email_html ); $email_html = str_replace( '{{starter_type_found}}', __( 'Our Vulnerability Monitor for WordPress found a potential security risk in your WordPress installation on', 'onecom-wp' ), $email_html ); $siteLink = "" . $site_url_inside . ''; if ( 'vulsFound' === $type ) { $email_html = str_replace( '{{disclaimer}}', $this->prepareDisclaimer( $type, $items ), $email_html ); $email_html = str_replace( '{{fix_title}}', __( 'To fix this:', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{fix_steps}}', $this->mailDescription(), $email_html ); $email_html = str_replace( '{{vulnerabilities_title}}', __( 'Vulnerabilities', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{update_now}}', __( 'Update now', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{see_more_cta}}', __( 'See more', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{upgrade_mwp_upsell_text}}', sprintf( __( 'Upgrade to %sManaged WordPress%s to automatically fix vulnerabilities.', 'onecom-wp' ), "", '' ), $email_html ); $email_html = str_replace( '{{learn_cta}}', __( 'Learn more', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{tip_auto_update}}', __( 'Tip! Enable the auto-update feature for the Vulnerability monitor in your WordPress dashboard to fix issues automatically.', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{tip_link_text}}', __( 'Click here to enable auto-update feature.', 'onecom-wp' ), $email_html ); } elseif ( 'vulsFixed' === $type ) { $locale = explode( '_', get_locale() )[0]; if ( ! $locale || ! array_key_exists( $locale, $this->guide_links ) ) { $locale = 'en'; } $email_html = str_replace( '{{vm_fixed_title}}', __( 'Vulnerabilities fixed', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{starter_type_fixed}}', sprintf( __( 'Our Vulnerability Monitor for WordPress fixed a potential security risk in your WordPress installation on %s by updating the below mentioned.', 'onecom-wp' ), $siteLink ), $email_html ); $link = ''; $recomm_text = sprintf( __( 'We recommend that you check your website to see if everything is still working as expected. If you experience issues, please follow our guide to %sroll back to a previous version%s.', 'onecom-wp' ), $link, '' ); $recomm_text .= '

'; $recomm_text .= __( "Note that vulnerabilities can exist both in your live website and staging environment. Please, do a manual update if you don't have automatic updates enabled in both locations.", 'onecom-wp' ); $email_html = str_replace( '{{fix_steps}}', $recomm_text, $email_html ); $email_html = str_replace( '{{vulnerabilities_title}}', __( 'Fixed vulnerabilities', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{view_site}}', __( 'View site', 'onecom-wp' ), $email_html ); // Vuls found but no fix version } else { $recomm_text = sprintf( __( 'Our Vulnerability Monitor for WordPress found a potential security risk in your WordPress installation on %s, but was not able to automatically update the vulnerable plugin/theme to the latest version. This likely means the plugin/theme is outdated and no longer actively maintained by the developers.', 'onecom-wp' ), $siteLink ); $recomm_text .= '

'; $recomm_text .= __( 'We recommend you remove the vulnerable plugin or theme and look for an alternative instead. Note that vulnerabilities can exist both in your live website and staging environment.', 'onecom-wp' ); $email_html = str_replace( '{{fix_steps}}', $recomm_text, $email_html ); $email_html = str_replace( '{{vulnerabilities_title}}', __( 'Vulnerabilities', 'onecom-wp' ), $email_html ); $email_html = str_replace( '{{view_site}}', __( 'View site', 'onecom-wp' ), $email_html ); } $admin_url = is_multisite() ? get_admin_url() : admin_url(); $update_link = $admin_url . 'update-core.php?force-check=1'; $email_html = str_replace( '{{admin_url}}', $admin_url, $email_html ); $email_html = str_replace( '{{update_link}}', $update_link, $email_html ); $vuls_html = ''; //Update unsubscribe link $email_html = str_replace( '{{vm_page_link}}', $this->vm_page_link, $email_html ); $email_html = str_replace( '{{vm_list_page}}', $this->vm_list_link, $email_html ); if ( 'vulsFound' === $type || 'vulsNotAutoFixed' === $type ) { $items_arr = array(); if ( ! empty( $items['plugins'] ) ) { $items_arr = array_merge( $items_arr, $items['plugins'] ); } if ( ! empty( $items['themes'] ) ) { $items_arr = array_merge( $items_arr, $items['themes'] ); } if ( ! empty( $items['wp'] ) ) { $items_arr['WordPress core'] = $items['wp']; } foreach ( $items_arr as $key => $item ) { if ( array_key_exists( 'plugins', $items ) && in_array( $item, $items['plugins'] ) ) { $item_type = 'plugins'; } elseif ( array_key_exists( 'themes', $items ) && in_array( $item, $items['themes'] ) ) { $item_type = 'themes'; } elseif ( array_key_exists( 'wp', $items ) && in_array( $item, $items['wp'] ) ) { $item_type = 'core'; } //skip all vuls that have any fix version from the vuls couldn't be fixed automatically email if ( 'vulsNotAutoFixed' === $type && $item['fixed_in'] !== '' ) { unset( $items_arr[ $key ] ); continue; } $max_cvss_score = max( array_column( $item['vulnerabilities'], 'cvss_score' ) ); $final_cvss_score = is_numeric( $max_cvss_score ) ? $max_cvss_score : 10; $count_vul = count( $item['vulnerabilities'] ); $exploitable_vul = array_search( true, array_column( $item['vulnerabilities'], 'is_exploited' ) ); $vuls_html .= '' . $item['name'] . ' v' . $item['installed_version'] . ''; $vuls_html .= $this->get_vul_severity_label( $final_cvss_score ); if ( $this->settings->isPremium() && $exploitable_vul !== false && $count_vul > 1 ) { if ( $item_type === 'plugins' ) { $vuls_html .= '

' . sprintf( __( '%s has a vulnerability that is known to be exploited', 'onecom-wp' ), __( 'This plugin', 'onecom-wp' ) ) . '

'; } elseif ( $item_type === 'themes' ) { $vuls_html .= '

' . sprintf( __( '%s has a vulnerability that is known to be exploited', 'onecom-wp' ), __( 'This theme', 'onecom-wp' ) ) . '

'; } elseif ( $item_type === 'core' ) { $vuls_html .= '

' . sprintf( __( '%s has a vulnerability that is known to be exploited', 'onecom-wp' ), __( 'This core version', 'onecom-wp' ) ) . '

'; } } elseif ( $this->settings->isPremium() && $exploitable_vul !== false && $count_vul === 1 ) { $vuls_html .= '

' . __( 'This vulnerability is known to be exploited', 'onecom-wp' ) . '

'; } $vuls_html .= "'; } } elseif ( 'vulsFixed' === $type ) { // iterate through items $now_updated_text = __( 'now updated to %s', 'onecom-wp' ); foreach ( $items as $item ) { $updateVer = sprintf( $now_updated_text, 'v' . $item['new_version'] ); $vuls_html .= '' . $item['name'] . ' v' . $item['old_version'] . ' (' . $updateVer . ')' . ''; $vuls_html .= ''; } } // put things together $email_html = str_replace( '{{vulnerabilities}}', $vuls_html, $email_html ); return $email_html; } /** * Returns vulnerability severity lebel: critical, high, medium or low */ public function get_vul_severity_label( $cvss ): string { if ( $cvss < 4 ) { $severity_label = "" . __( 'Low', 'onecom-wp' ) . ''; } elseif ( $cvss < 7 ) { $severity_label = "" . __( 'Medium', 'onecom-wp' ) . ''; } elseif ( $cvss < 9 ) { $severity_label = "" . __( 'High', 'onecom-wp' ) . ''; } else { $severity_label = "" . __( 'Critical', 'onecom-wp' ) . ''; } return $severity_label; } /** * Get recipient emails */ public function getRecipients(): array { $settings = $this->settings->get(); $notify_admins = $settings['settings']['notify_admin']; $emails = array(); if ( 1 === $notify_admins ) { // WP_User_Query arguments $args = array( 'role' => 'Administrator', 'fields' => array( 'user_email' ), ); // The User Query $user_query = new WP_User_Query( $args ); $all_users = $user_query->results; foreach ( $all_users as $user ) { $emails[] = $user->user_email; } } // merge custom emails into all emails if ( ! empty( $settings['settings']['custom_emails'] ) ) { $emails = array_merge( $emails, $settings['settings']['custom_emails'] ); } // filter emails to prevent sending mailers to support if ( ! empty( $emails ) ) { $emails = $this->oci_support_mail_filter( $emails ); } return $emails; } // Filter VM emails to exclude support mails public function oci_support_mail_filter( $emails ): array { $args = array( 'body' => wp_json_encode( $emails ), ); $filtered_emails = wp_remote_post( $this->support_mail_filter_api, $args ); $response_body = json_decode( wp_remote_retrieve_body( $filtered_emails ), true ); $response_code = wp_remote_retrieve_response_code( $filtered_emails ); // If valid response found, return filtered emails // if all emails excluded (null returned via api), send empty array if ( $response_code === 200 && is_array( $response_body ) && $response_body['success'] ) { return $response_body['data'] ?? array(); } else { // if api error, return original emails return $emails; } } /** * Remove update-attempt records once the email is sent */ public function removeAttemptRecords( $type = 'successful' ) { // get latest state from DB $settings = $this->settings->get(); // remove the item for which we have attempted an update unset( $settings['update_attempts'][ $type ] ); $this->settings->update( $settings ); } /** * Default mail headers for Vulnerability monitor emails */ public function getMailHeaders( $recipients = array() ): string { // get site URL and parse it $siteHostName = wp_parse_url( get_site_url() ); // get hostname from site URL $siteHostName = $siteHostName['host']; // add header X-OneCom-Vmon along with default mail headers. More details: WPIN-2823 $headers = 'Content-Type: text/html;' . "\r\n"; $headers .= 'charset=UTF-8' . "\r\n"; $headers .= 'X-OneCom-Vmon:' . $siteHostName . "\r\n"; // add Cc headers foreach ( $recipients as $recipient ) { $headers .= 'Bcc: ' . $recipient . "\r\n"; } error_log( 'Prepared these headers: ' . json_encode( $headers ) ); return $headers; } /** * Vuls fixed mail */ public function vulsFixedMail( $type ) { $vuls = $this->settings->get(); if ( empty( $vuls['update_attempts']['successful'] ) ) { error_log( 'Skipping send_mail by rule...[Could not find any successful update attempt]' ); return; } $email_html = $this->prepareEmail( $type, $vuls['update_attempts']['successful'] ); $emailSubject = $this->prepareSubject( $type ); $recipients = $this->getRecipients(); $to = $recipients[0]; unset( $recipients[0] ); $headers = $this->getMailHeaders( $recipients ); add_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); $mail_sent = wp_mail( $to, $emailSubject, $email_html, $headers ); remove_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); if ( $mail_sent ) { error_log( '~~~ Email sent successfully!~~~' ); $this->{$type . 'Stats'}( $vuls['update_attempts'] ); $this->removeAttemptRecords(); return true; } else { error_log( "FAILED to send emails vulsFixedMail $emailSubject" ); return false; } } /** * Vulnerability not auto fixed * @param $type * @return bool */ public function vulsNotAutoFixedMail( $type ) { $vuls = $this->settings->get(); $actual_vul = $vuls['vulnerabilities']; if ( empty( $actual_vul['wp'] ) && empty( $actual_vul['plugins'] ) && empty( $actual_vul['themes'] ) ) { error_log( 'Skip sending email if no vulnerability found' ); return; } $email_html = $this->prepareEmail( $type, $actual_vul ); $emailSubject = $this->prepareSubject( $type ); $recipients = $this->getRecipients(); $to = $recipients[0]; unset( $recipients[0] ); $headers = $this->getMailHeaders( $recipients ); add_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); $mail_sent = wp_mail( $to, $emailSubject, $email_html, $headers ); remove_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); if ( $mail_sent ) { error_log( '~~~ Email sent successfully!~~~' ); //stats for vmNotAutoFixed $this->{$type . 'Stats'}( $actual_vul ); return true; } else { error_log( "FAILED to send emails in vulsNotAutoFixed $emailSubject" ); return false; } } /** * Vuls found mail */ public function vulsFoundMail( $type ) { $vuls = $this->settings->get(); if ( empty( $vuls['vulnerabilities'] ) ) { error_log( 'Skipping send_mail by rule...[Could not find any vulnerabilities]' ); return false; } $email_html = $this->prepareEmail( $type, $vuls['vulnerabilities'] ); $emailSubject = $this->prepareSubject( $type ); $recipients = $this->getRecipients(); $to = $recipients[0]; unset( $recipients[0] ); $headers = $this->getMailHeaders( $recipients ); add_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); $mail_sent = wp_mail( $to, $emailSubject, $email_html, $headers ); remove_filter( 'wp_mail_from_name', array( $this, 'mail_from_name' ) ); if ( $mail_sent ) { error_log( '~~~ Email sent successfully!~~~' ); $this->{$type . 'Stats'}( $vuls['vulnerabilities'] ); return true; } else { error_log( "FAILED to send email $emailSubject" ); return false; } } /** * Prepare and Send Email */ public function sendEmail( $type = 'vulsFound' ) { $this->{$type . 'Mail'}( $type ); } /** * Send stats for vulsNotAutoFixed * @param $vuls * @return void */ public function vulsNotAutoFixedStats( $vuls = array() ): void { if ( empty( $vuls ) ) { return; } $slugs = array(); //Check is fixed in missing in WP if ( array_key_exists( 'wp', $vuls ) && ! empty( $vuls['wp'] ) && '' === min( array_column( $vuls['wp']['vulnerabilities'], 'fixed_in' ) ) ) { $slugs['wp'] = $vuls['wp']; } foreach ( $vuls as $key => $items ) { foreach ( $items as $slug => $item ) { if ( empty( $item['vulnerabilities'] ) ) { continue; } //continue if fixed_in not empty if ( '' !== min( array_column( $item['vulnerabilities'], 'fixed_in' ) ) ) { continue; } $slugs[ $key ][] = array( 'slug' => $slug, 'version' => max( array_column( $item['vulnerabilities'], 'introduced_in' ) ), ); } } class_exists( 'OCPushStats' ) ? OCPushStats::push_vul_monitor_stats( 'lookup', 'setting', 'vulnerability_monitor', array( 'vmNotFixedIn' => $slugs ) ) : ''; } /** * Send vulsFound stats * @param array $vuls */ public function vulsFoundStats( $vuls = array() ): void { if ( empty( $vuls ) ) { return; } $slugs = array(); if ( array_key_exists( 'wp', $vuls ) ) { $slugs['wp'] = $vuls['wp']; } foreach ( $vuls as $key => $items ) { foreach ( $items as $slug => $item ) { if ( empty( $item['vulnerabilities'] ) ) { continue; } $slugs[ $key ][] = array( 'slug' => $slug, 'version' => max( array_column( $item['vulnerabilities'], 'introduced_in' ) ), ); } } class_exists( 'OCPushStats' ) ? OCPushStats::push_vul_monitor_stats( 'lookup', 'setting', 'vulnerability_monitor', array( 'vulnerabilities' => $slugs ) ) : ''; } /** * Send vulsFixed stats * @param array $vuls */ public function vulsFixedStats( $attempts = array() ): void { if ( empty( $attempts['successful'] ) ) { return; } class_exists( 'OCPushStats' ) ? OCPushStats::push_vul_monitor_stats( 'upgrade', 'setting', 'vulnerability_monitor', array( 'vulnerabilities' => $attempts ) ) : ''; } /** * Change the sender name in emails * @return string */ public function mail_from_name() { return 'WordPress Vulnerability Monitor'; } }