Sindbad~EG File Manager
<?php
namespace WPSecurityNinja\Plugin;
if ( !function_exists( 'add_action' ) ) {
die( 'Please don\'t open this file directly!' );
}
define( 'WF_SN_VU_OPTIONS_NAME', 'wf_sn_vu_settings_group' );
define( 'WF_SN_VU_OPTIONS_KEY', 'wf_sn_vu_settings' );
define( 'WF_SN_VU_RESULTS_KEY', 'wf_sn_vu_results' );
define( 'WF_SN_VU_VULNS_NOTICE', 'wf_sn_vu_vulns_notice' );
// used for showing notices
define( 'WF_SN_VU_OUTDATED', 'wf_sn_vu_outdated' );
define( 'WF_SN_VU_LAST_EMAIL', 'wf_sn_vu_last_email' );
// Last time an email was sent
class wf_sn_vu
{
public static $options ;
public static $vu_api_url = 'https://wpsecurityninja.sfo2.cdn.digitaloceanspaces.com/vulnerabilities.json' ;
public static $api_urls = array(
'plugins' => 'https://wpsecurityninja.sfo2.cdn.digitaloceanspaces.com/plugin_vulns.jsonl',
'themes' => 'https://wpsecurityninja.sfo2.cdn.digitaloceanspaces.com/theme_vulns.jsonl',
'wordpress' => 'https://wpsecurityninja.sfo2.cdn.digitaloceanspaces.com/wp_vulns.jsonl',
) ;
/**
* init plugin
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Tuesday, January 12th, 2021.
* @access public static
* @return void
*/
public static function init()
{
self::$options = self::get_options();
add_action( 'admin_init', array( __NAMESPACE__ . '\\wf_sn_vu', 'admin_init' ) );
add_filter( 'sn_tabs', array( __NAMESPACE__ . '\\wf_sn_vu', 'sn_tabs' ) );
add_action( 'admin_notices', array( __NAMESPACE__ . '\\wf_sn_vu', 'admin_notice_vulnerabilities' ) );
add_action( 'init', array( __NAMESPACE__ . '\\wf_sn_vu', 'schedule_cron_jobs' ) );
add_action( 'secnin_update_vuln_list', array( __NAMESPACE__ . '\\wf_sn_vu', 'update_vuln_list' ) );
add_action(
'upgrader_process_complete',
array( __NAMESPACE__ . '\\wf_sn_vu', 'do_action_upgrader_process_complete' ),
10,
2
);
add_action( 'delete_theme', array( __NAMESPACE__ . '\\wf_sn_vu', 'do_action_upgrader_process_complete' ) );
add_action( 'delete_plugin', array( __NAMESPACE__ . '\\wf_sn_vu', 'do_action_upgrader_process_complete' ) );
}
/**
* do_action_upgrader_process_complete.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, May 13th, 2022.
* @access public static
* @return void
*/
public static function do_action_upgrader_process_complete()
{
$options = self::get_options();
// No update if feature disabled
if ( $options['enable_vulns'] ) {
// deletes the transient before checking again
delete_transient( 'wf_sn_return_vulnerabilities' );
// Updates the vuln list
self::return_vulnerabilities();
}
}
/**
* get_options.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return mixed
*/
public static function get_options()
{
$options = get_option( WF_SN_VU_OPTIONS_NAME );
$defaults = array();
if ( !$options ) {
$options = array(
'enable_vulns' => 1,
'enable_outdated' => 0,
'enable_admin_notification' => 1,
'enable_email_notice' => 0,
'email_notice_recipient' => '',
);
}
$return = array_merge( $defaults, $options );
return $return;
}
/**
* Register settings on admin init
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return void
*/
public static function admin_init()
{
register_setting( WF_SN_VU_OPTIONS_NAME, WF_SN_VU_OPTIONS_NAME, array( __NAMESPACE__ . '\\wf_sn_vu', 'sanitize_settings' ) );
}
/**
* Schedule cron jobs
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return void
*/
public static function schedule_cron_jobs()
{
if ( !wp_next_scheduled( 'secnin_update_vuln_list' ) ) {
wp_schedule_event( time() + 10, 'daily', 'secnin_update_vuln_list' );
}
}
/**
* Tab filter
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @param mixed $tabs
* @return mixed
*/
public static function sn_tabs( $tabs )
{
$vuln_tab = array(
'id' => 'sn_vuln',
'class' => '',
'label' => __( 'Vulnerabilities', 'security-ninja' ),
'callback' => array( __NAMESPACE__ . '\\wf_sn_vu', 'render_vuln_page' ),
);
// Add number of vulns to the tab list
$options = self::get_options();
// Check if notification bubles enabled.
if ( $options['enable_admin_notification'] ) {
$return_vuln_count = self::return_vuln_count();
if ( $return_vuln_count ) {
$vuln_tab['count'] = $return_vuln_count;
}
}
$done = 0;
$tabcount = count( $tabs );
for ( $i = 0 ; $i < $tabcount ; $i++ ) {
if ( 'sn_vuln' === $tabs[$i]['id'] ) {
$tabs[$i] = $vuln_tab;
$done = 1;
break;
}
}
if ( !$done ) {
$tabs[] = $vuln_tab;
}
return $tabs;
}
/**
* Strips http:// or https://
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @global
* @param string $url Default: ''
* @return mixed
*/
public static function remove_http( $url = '' )
{
if ( 'http://' === $url || 'https://' === $url ) {
return $url;
}
$matches = substr( $url, 0, 7 );
if ( 'http://' === $matches ) {
$url = substr( $url, 7 );
} else {
$matches = substr( $url, 0, 8 );
if ( 'https://' === $matches ) {
$url = substr( $url, 8 );
}
}
return $url;
}
/**
* Function to get the file and save it locally.
*
* @author Unknown
* @since v0.0.1
* @version v1.0.0 Tuesday, July 25th, 2023.
* @version v1.0.1 Friday, October 13th, 2023.
* @access public static
* @param mixed $file_content
* @param mixed $filename
* @return boolean
*/
public static function get_file_and_save( $file_content, $filename )
{
if ( !$file_content ) {
return false;
}
global $wp_filesystem ;
if ( empty($wp_filesystem) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
}
// Get filesystem credentials
$url = wp_nonce_url( "plugins.php" );
if ( false === ($creds = request_filesystem_credentials(
$url,
'',
false,
false,
null
)) ) {
return;
// Failed to get credentials
}
if ( !WP_Filesystem( $creds ) ) {
request_filesystem_credentials(
$url,
'',
true,
false,
null
);
// Failed to connect, ask for credentials again
return false;
}
$upload_dir = wp_upload_dir();
$secninja_upload_dir = $upload_dir['basedir'] . '/security-ninja/';
$dir_path = $secninja_upload_dir . 'vulns/';
if ( !$wp_filesystem->exists( $secninja_upload_dir ) ) {
$wp_filesystem->mkdir( $secninja_upload_dir );
$wp_filesystem->mkdir( $dir_path );
}
if ( !$wp_filesystem->exists( $dir_path ) ) {
$wp_filesystem->mkdir( $dir_path );
}
if ( !$wp_filesystem->is_dir( $dir_path ) ) {
if ( !self::recursive_mkdir( $dir_path, $wp_filesystem ) ) {
return false;
}
}
// Define the path to save the file to.
$file_path = $dir_path . $filename;
$result = $wp_filesystem->put_contents( $file_path, $file_content, FS_CHMOD_FILE );
if ( $result === false ) {
return false;
}
add_option( WF_SN_VU_OUTDATED, time() );
return true;
}
/**
* Function to recursively create directories
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Thursday, October 12th, 2023.
* @access public static
* @param mixed $dir
* @param mixed $wp_filesystem
* @param mixed $mode Default: FS_CHMOD_DIR
* @return boolean
*/
public static function recursive_mkdir( $dir, $wp_filesystem, $mode = FS_CHMOD_DIR )
{
$dirs = explode( '/', $dir );
$path = '';
foreach ( $dirs as $part ) {
$path .= "{$part}/";
if ( !$wp_filesystem->is_dir( $path ) ) {
if ( !$wp_filesystem->mkdir( $path, $mode ) ) {
return false;
// Failed to make directory
}
}
}
return true;
}
/**
* Function to read the content of the file.
*
* @author Lars Koudal
* @since v5.160
* @version v1.0.0 Tuesday, July 25th, 2023.
* @access public static
* @global
* @return mixed
*/
public static function load_vulnerabilities()
{
global $wp_filesystem ;
if ( !function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
global $wp_filesystem ;
if ( empty($wp_filesystem) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
}
$upload_dir = wp_upload_dir();
$data = new \stdClass();
$types = [ 'wordpress', 'plugins', 'themes' ];
foreach ( $types as $type ) {
$file_path = $upload_dir['basedir'] . "/security-ninja/vulns/{$type}_vulns.jsonl";
// Check if the file exists.
if ( !$wp_filesystem->exists( $file_path ) ) {
$data->{$type} = [];
continue;
}
// Initialize an empty array to hold the data for this type.
$data_for_type = [];
// Read the file line by line.
$file = $wp_filesystem->get_contents_array( $file_path );
if ( $file ) {
foreach ( $file as $line ) {
$data_for_type[] = json_decode( $line );
}
}
$data->{$type} = $data_for_type;
}
if ( !$data ) {
return false;
}
// $data->timestamp = current_time('timestamp');
return $data;
}
/**
* set_html_content_type.
*
* @author Unknown
* @since v0.0.1
* @version v1.0.0 Sunday, October 29th, 2023.
* @return mixed
*/
public static function set_html_content_type()
{
return 'text/html';
}
/**
* Updates the vulnerability list.
* Creates the folder if necessary.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @version v1.0.1 Sunday, September 4th, 2022.
* @access public static
* @return void
*/
public static function update_vuln_list()
{
$options = self::get_options();
// No update if feature disabled
if ( !$options['enable_vulns'] ) {
return false;
}
$oldcount = false;
$newcount = false;
$old_data = self::load_vulnerabilities();
$oldcount = 0;
if ( $old_data ) {
$oldcount = self::return_known_vuln_count();
}
foreach ( self::$api_urls as $type => $url ) {
// Get list of vulnerabilities from API
$request_url = add_query_arg( 'ver', wf_sn::$version, $url );
$response = wp_remote_get( $request_url );
if ( !is_wp_error( $response ) ) {
$body = wp_remote_retrieve_body( $response );
if ( !$body ) {
$body = '';
}
$result = self::get_file_and_save( $body, "{$type}_vulns.jsonl" );
} else {
}
}
$newcount = self::return_known_vuln_count();
// @todo - does it trigger properly?
if ( $oldcount && $newcount ) {
$diff = $newcount - $oldcount;
if ( 0 === $oldcount ) {
$diff = 1;
}
// Just in case the difference was 0
if ( 0 < $diff ) {
$message = '';
$message = esc_html( sprintf( _n(
'%s vulnerability added.',
'%s new vulnerabilites added.',
$diff,
'security-ninja'
), number_format_i18n( $diff ) ) );
$message .= ' ' . sprintf(
// translators: Shows how many vulnerabilities are known and when list was updated
esc_html__( 'Vulnerability list contains %1$s known vulnerabilities. Last updated %2$s', 'security-ninja' ),
esc_html( number_format_i18n( $newcount ) ),
esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $old_data->timestamp ) )
) . '';
update_option( WF_SN_VU_VULNS_NOTICE, $message, false );
}
}
$options = self::get_options();
$enable_email_notice = $options['enable_email_notice'];
// *** EMAIL WARNINGS - CHECK if an email should be sent...
$vulns = self::return_vulnerabilities();
if ( $vulns && $enable_email_notice && (!empty($vulns->plugins) || !empty($vulns->wordpress) || !empty($vulns->themes)) ) {
$last_email_sent = get_option( WF_SN_VU_LAST_EMAIL, false );
$send_email = false;
if ( $last_email_sent ) {
$difference_in_seconds = time() - $last_email_sent;
// Only send maximum one email per day.
if ( $difference_in_seconds > DAY_IN_SECONDS ) {
$send_email = true;
}
} else {
// Email never sent? Lets go.
$send_email = true;
}
if ( $send_email ) {
// Ready to send email
$email_notice_recipient = $options['email_notice_recipient'];
// Introduction: Why the user is receiving this email
$message_content = esc_html__( 'You are receiving this email because you have activated email warnings for the vulnerability scanner in your Security Ninja settings.', 'security-ninja' ) . PHP_EOL . PHP_EOL;
$message_content .= sprintf(
// translators: Shows how many vulnerabilities are known and when list was updated
esc_html__( 'Security Ninja has detected vulnerabilities on your website, %1$s', 'security-ninja' ),
wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES )
) . PHP_EOL;
if ( isset( $vulns['plugins'] ) ) {
foreach ( $vulns['plugins'] as $key => $vu ) {
$message_content .= esc_html__( 'Plugin', 'security-ninja' ) . ' ' . esc_html( $vu['name'] ) . PHP_EOL;
$message_content .= esc_html( $vu['desc'] ) . PHP_EOL;
if ( $vu['CVE_ID'] ) {
$message_content .= 'ID: ' . esc_html( $vu['CVE_ID'] ) . PHP_EOL;
}
$message_content .= PHP_EOL . PHP_EOL;
}
}
if ( isset( $vulns['themes'] ) ) {
foreach ( $vulns['themes'] as $key => $vu ) {
$message_content .= esc_html__( 'Theme', 'security-ninja' ) . ' ' . esc_html( $vu['name'] ) . PHP_EOL;
$message_content .= esc_html( $vu['desc'] ) . PHP_EOL;
if ( $vu['CVE_ID'] ) {
$message_content .= 'ID: ' . esc_html( $vu['CVE_ID'] ) . PHP_EOL;
}
$message_content .= PHP_EOL . PHP_EOL;
}
}
if ( $message_content !== '' ) {
$message_content .= esc_html__( 'View all vulnerabilities:', 'security-ninja' ) . ' ' . esc_url( admin_url( 'admin.php?page=wf-sn#sn_vuln' ) ) . PHP_EOL;
$message_content .= PHP_EOL;
$url = Wf_Sn::generate_sn_web_link( 'email_vuln_warning_footer', '/' );
$message_content .= esc_html__( 'Thank you for using WP Security Ninja', 'security-ninja' ) . ' - ' . esc_url( $url ) . PHP_EOL;
}
$site_url = site_url();
$parsed_url = parse_url( $site_url );
$domain = ( isset( $parsed_url['host'] ) ? $parsed_url['host'] : '' );
$subject = esc_html__( 'Vulnerabilities detected on', 'security-ninja' ) . ' ' . $domain;
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
add_filter( 'wp_mail_content_type', __NAMESPACE__ . '\\wf_sn_vu::set_html_content_type' );
$sendresult = wp_mail(
$email_notice_recipient,
$subject,
$message_content,
$headers
);
remove_filter( 'wp_mail_content_type', __NAMESPACE__ . '\\wf_sn_vu::set_html_content_type' );
update_option( WF_SN_VU_LAST_EMAIL, current_time( 'timestamp' ), false );
}
}
}
/**
* Check if an array is a multidimensional array.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @global
* @param mixed $x
* @return boolean
*/
public static function is_multi_array( $x )
{
if ( count( array_filter( $x, 'is_array' ) ) > 0 ) {
return true;
}
return false;
}
/**
* Convert an object to an array.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @global
* @param mixed $object The object to convert
* @return mixed
*/
public static function object_to_array_map( $object )
{
if ( !is_object( $object ) && !is_array( $object ) ) {
return $object;
}
return array_map( array( __NAMESPACE__ . '\\wf_sn_vu', 'object_to_array' ), (array) $object );
}
/**
* Check if a value exists in the array/object.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @global
* @param mixed $needle The value that you are searching for
* @param mixed $haystack The array/object to search
* @param boolean $strict Whether to use strict search or not
* @return boolean
*/
public static function search_for_value( $needle, $haystack, $strict = true )
{
$haystack = self::object_to_array( $haystack );
if ( is_array( $haystack ) ) {
if ( self::is_multi_array( $haystack ) ) {
// Multidimensional array
foreach ( $haystack as $subhaystack ) {
if ( self::search_for_value( $needle, $subhaystack, $strict ) ) {
return true;
}
}
} elseif ( array_keys( $haystack ) !== range( 0, count( $haystack ) - 1 ) ) {
// Associative array
foreach ( $haystack as $key => $val ) {
if ( $needle === $val && !$strict ) {
return true;
} elseif ( $needle === $val && $strict ) {
return true;
}
}
return false;
} else {
// Normal array
if ( $needle === $haystack && !$strict ) {
return true;
} elseif ( $needle === $haystack && $strict ) {
return true;
}
}
}
return false;
}
/**
* object_to_array.
* Ref: https://stackoverflow.com/questions/4345554/convert-a-php-object-to-an-associative-array
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Thursday, July 22nd, 2021.
* @param mixed $data
* @return mixed
*/
public static function object_to_array( $data )
{
if ( is_array( $data ) || is_object( $data ) ) {
$result = [];
foreach ( $data as $key => $value ) {
$result[$key] = ( is_array( $data ) || is_object( $data ) ? self::object_to_array( $value ) : $value );
}
return $result;
}
return $data;
}
/**
* Return list of known vulnerabilities from the website, checking installed plugins and WordPress version against list from API.
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @version v1.0.1 Friday, May 13th, 2022.
* @access public static
* @return array
*/
public static function return_vulnerabilities()
{
// Note - transient is deleted when updating settings.
//if (false === ($found_vulnerabilities = get_transient('wf_sn_return_vulnerabilities'))) {
global $wp_version ;
wf_sn::timerstart( 'scan_for_vulns' );
$vuln_plugin_arr = false;
$installed_plugins = false;
$options = self::get_options();
if ( $options['enable_vulns'] ) {
$vulns = self::load_vulnerabilities();
if ( !$vulns ) {
self::update_vuln_list();
$vulns = self::load_vulnerabilities();
}
// offers problem here on free version? memory issue, maxes out 256mb
$vuln_plugin_arr = self::object_to_array( $vulns->plugins );
$installed_plugins = get_plugins();
}
// Tests for plugin problems
if ( $installed_plugins && $vuln_plugin_arr ) {
$found_vulnerabilities = array();
foreach ( $installed_plugins as $key => $ap ) {
$lookup_id = strtok( $key, '/' );
$findplugin = array_search( $lookup_id, array_column( $vuln_plugin_arr, 'slug' ), true );
if ( $findplugin ) {
if ( isset( $vuln_plugin_arr[$findplugin]['versionEndExcluding'] ) && '' !== $vuln_plugin_arr[$findplugin]['versionEndExcluding'] ) {
// check #1 - versionEndExcluding
if ( version_compare( $ap['Version'], $vuln_plugin_arr[$findplugin]['versionEndExcluding'], '<' ) ) {
$description = '';
if ( isset( $vuln_plugin_arr[$findplugin]['description'] ) ) {
$description = $vuln_plugin_arr[$findplugin]['description'];
}
$found_vulnerabilities['plugins'][$lookup_id] = array(
'name' => $ap['Name'],
'desc' => $description,
'installedVersion' => $ap['Version'],
'versionEndExcluding' => $vuln_plugin_arr[$findplugin]['versionEndExcluding'],
'CVE_ID' => $vuln_plugin_arr[$findplugin]['CVE_ID'],
'refs' => $vuln_plugin_arr[$findplugin]['refs'],
);
}
}
// Checks via the versionImpact method
if ( isset( $vuln_plugin_arr[$findplugin]['versionImpact'] ) && '' !== $vuln_plugin_arr[$findplugin]['versionImpact'] ) {
if ( version_compare( $ap['Version'], $vuln_plugin_arr[$findplugin]['versionImpact'], '<=' ) ) {
$found_vulnerabilities['plugins'][$lookup_id] = array(
'name' => $ap['Name'],
'desc' => $vuln_plugin_arr[$findplugin]['description'],
'installedVersion' => $ap['Version'],
'versionImpact' => $vuln_plugin_arr[$findplugin]['versionImpact'],
'CVE_ID' => $vuln_plugin_arr[$findplugin]['CVE_ID'],
'refs' => $vuln_plugin_arr[$findplugin]['refs'],
);
if ( isset( $vuln_plugin_arr[$findplugin]['recommendation'] ) ) {
$found_vulnerabilities['plugins'][$lookup_id]['recommendation'] = $vuln_plugin_arr[$findplugin]['recommendation'];
}
}
}
}
}
}
// ------------ Find WordPress vulnerabilities ------------
$wordpressarr = false;
if ( isset( $vulns->wordpress ) ) {
$wordpressarr = self::object_to_array( $vulns->wordpress );
}
$lookup_id = 0;
if ( $wordpressarr ) {
foreach ( $wordpressarr as $key => $wpvuln ) {
if ( version_compare( $wp_version, $wpvuln['versionEndExcluding'], '<' ) ) {
$desc = '';
if ( isset( $wpvuln['description'] ) ) {
$desc = $wpvuln['description'];
}
$found_vulnerabilities['wordpress'][$lookup_id] = array(
'desc' => $desc,
'versionEndExcluding' => $wpvuln['versionEndExcluding'],
'CVE_ID' => $wpvuln['CVE_ID'],
);
if ( isset( $wpvuln['recommendation'] ) ) {
$found_vulnerabilities['wordpress'][$lookup_id]['recommendation'] = $wpvuln['recommendation'];
}
$lookup_id++;
}
}
}
// Find vulnerable themes
// Build new empty Array to store the themes
$themes = array();
// Loads theme data
$all_themes = wp_get_themes();
// Build theme data manually
foreach ( $all_themes as $theme ) {
$themes[$theme->stylesheet] = array(
'Name' => $theme->get( 'Name' ),
'Author' => $theme->get( 'Author' ),
'AuthorURI' => $theme->get( 'AuthorURI' ),
'Version' => $theme->get( 'Version' ),
'Template' => $theme->get( 'Template' ),
'Status' => $theme->get( 'Status' ),
);
}
$vuln_theme_arr = false;
if ( isset( $vulns->themes ) ) {
$vuln_theme_arr = self::object_to_array( $vulns->themes );
}
if ( $themes && $vuln_theme_arr ) {
foreach ( $themes as $key => $ap ) {
$findtheme = array_search( $key, array_column( $vuln_theme_arr, 'slug' ), true );
if ( $findtheme !== false ) {
// $Matched theme array with details
$matched = $vuln_theme_arr[$findtheme];
if ( isset( $matched['versionEndExcluding'] ) && '' !== $vuln_theme_arr[$findtheme]['versionEndExcluding'] ) {
// check #1 - versionEndExcluding
if ( version_compare( $ap['Version'], $matched['versionEndExcluding'], '<' ) ) {
$desc = '';
if ( isset( $matched['description'] ) ) {
$desc = $matched['description'];
}
$found_vulnerabilities['themes'][$key] = array(
'name' => $ap['Name'],
'desc' => $desc,
'installedVersion' => $ap['Version'],
'versionEndExcluding' => $matched['versionEndExcluding'],
'CVE_ID' => $matched['CVE_ID'],
'refs' => $matched['refs'],
);
}
}
}
}
// 2 - Lookup child themes (look by Template value) @todo!
}
$scan_time = wf_sn::timerstop( 'scan_for_vulns' );
if ( $scan_time ) {
$found_vulnerabilities['scan_time'] = $scan_time;
}
if ( $found_vulnerabilities ) {
return $found_vulnerabilities;
} else {
return false;
}
}
/**
* Gets list of WordPress from official API and their security status
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @version v1.0.1 Wednesday, January 13th, 2021.
* @access public static
* @return mixed
*/
public static function get_wp_ver_status()
{
// returns false if module disabled
$options = self::get_options();
if ( !$options['enable_vulns'] ) {
return false;
}
$wp_vers_status = get_transient( 'wp_vers_status' );
if ( false === $wp_vers_status ) {
$request_url = 'https://api.wordpress.org/core/stable-check/1.0/';
$response = wp_remote_get( $request_url );
if ( !is_wp_error( $response ) ) {
$body = wp_remote_retrieve_body( $response );
$wp_vers_status = json_decode( $body );
}
set_transient( 'wp_vers_status', $wp_vers_status, 12 * HOUR_IN_SECONDS );
}
return $wp_vers_status;
}
/**
* Returns the number of known vulnerabilities
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Tuesday, July 6th, 2021.
* @access public static
* @global
* @return int
*/
public static function return_known_vuln_count()
{
$vulns = self::load_vulnerabilities();
if ( !$vulns ) {
return false;
}
$plugin_vulns_count = 0;
$theme_vulns_count = 0;
$wp_vulns_count = 0;
if ( isset( $vulns->plugins ) ) {
$plugin_vulns_count = count( $vulns->plugins );
}
if ( isset( $vulns->themes ) ) {
$theme_vulns_count = count( $vulns->themes );
}
if ( isset( $vulns->wordpress ) ) {
$wp_vulns_count = count( $vulns->wordpress );
}
$total_vulnerabilities = $plugin_vulns_count + $wp_vulns_count + $theme_vulns_count;
return $total_vulnerabilities;
}
/**
* Returns number of known vulnerabilities across all types
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return mixed
*/
public static function return_vuln_count()
{
$vulnerabilities = self::return_vulnerabilities();
if ( !$vulnerabilities ) {
return false;
}
$total_vulnerabilities = 0;
if ( isset( $vulnerabilities['plugins'] ) ) {
$total_vulnerabilities = $total_vulnerabilities + count( $vulnerabilities['plugins'] );
}
if ( isset( $vulnerabilities['themes'] ) ) {
$total_vulnerabilities = $total_vulnerabilities + count( $vulnerabilities['themes'] );
}
if ( isset( $vulnerabilities['wordpress'] ) ) {
$total_vulnerabilities = $total_vulnerabilities + count( $vulnerabilities['wordpress'] );
}
return $total_vulnerabilities;
}
/**
* Renders vulnerability tab
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @version v1.0.1 Tuesday, January 10th, 2023.
* @access public static
* @return void
*/
public static function render_vuln_page()
{
global $wp_version ;
$options = self::get_options();
if ( $options['enable_vulns'] ) {
// Get the list of vulnerabilities
$vulnerabilities = self::return_vulnerabilities();
$vulns = self::load_vulnerabilities();
if ( !$vulns ) {
self::update_vuln_list();
$vulns = self::load_vulnerabilities();
}
$plugin_vulns_count = count( $vulns->plugins );
$theme_vulns_count = count( $vulns->themes );
$wp_vulns_count = count( $vulns->wordpress );
$total_vulnerabilities = $plugin_vulns_count + $wp_vulns_count + $theme_vulns_count;
// Used for the output of WordPress version being used
$wp_status = '';
}
?>
<div class="submit-test-container">
<div class="card">
<h3><?php
esc_html_e( 'Vulnerability Scanner', 'security-ninja' );
?></h3>
<p><?php
echo esc_html__( 'Warns you of any known vulnerabilities in the plugins and themes you have installed.', 'security-ninja' ) . ' <a href="' . esc_url( wf_sn::generate_sn_web_link( 'help_improve', '/docs/vulnerabilities/scanner/' ) ) . '" target="_blank" rel="noopener">' . esc_html__( 'Click here to learn more', 'security-ninja' ) . '</a>' ;
?></p>
<?php
if ( isset( $vulnerabilities['wordpress'] ) or isset( $vulnerabilities['plugins'] ) or isset( $vulnerabilities['themes'] ) ) {
?>
<h2><?php
esc_html_e( 'Vulnerabilities found on your system!', 'security-ninja' );
?></h2>
<?php
if ( isset( $vulnerabilities['wordpress'] ) ) {
$get_wp_ver_status = self::get_wp_ver_status();
if ( isset( $get_wp_ver_status->{$wp_version} ) ) {
if ( 'insecure' === $get_wp_ver_status->{$wp_version} ) {
$wp_status = 'This version of WordPress (' . $wp_version . ') is considered <strong>INSECURE</strong>. You should upgrade as soon possible.';
// @i8n @todo
}
if ( 'outdated' === $get_wp_ver_status->{$wp_version} ) {
$wp_status = 'This version of WordPress (' . $wp_version . ') is considered <strong>OUTDATED</strong>. You should upgrade as soon possible.';
// @i8n
}
}
// @i8n BELOW
?>
<div class="vuln vulnwordpress">
<p>You are running WordPress version <?php
echo esc_html( $wp_version ) ;
?> and there are known vulnerabilities that
have been fixed in later versions. You should upgrade WordPress as soon as possible.</p>
<?php
if ( '' !== $wp_status ) {
?>
<div class="vulnrecommendation">
<h2>
<?php
echo $wp_status ;
//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
</h2>
</div>
<?php
}
?>
<p><?php
_e( 'Known vulnerabilities', 'security-ninja' );
?></p>
<?php
foreach ( $vulnerabilities['wordpress'] as $key => $wpvuln ) {
if ( isset( $wpvuln['versionEndExcluding'] ) ) {
?>
<h3><span class="dashicons dashicons-warning"></span> <?php
echo esc_html( 'WordPress ' . $wpvuln['CVE_ID'] ) ;
?></h3>
<div class="wrap-collabsible">
<input id="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="toggle" type="checkbox">
<label for="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="lbl-toggle">Details</label>
<div class="collapsible-content">
<div class="content-inner">
<?php
if ( isset( $wpvuln['desc'] ) && '' !== $wpvuln['desc'] ) {
?>
<p class="vulndesc"><?php
echo esc_html( $wpvuln['desc'] ) ;
?></p>
<?php
}
?>
<p class="vulnDetails">
<?php
printf(
/* translators: 1: WordPress version */
__( 'Fixed in WordPress version %1$s', 'security-ninja' ),
esc_html( $wpvuln['versionEndExcluding'] )
);
?>
</p>
<?php
if ( isset( $wpvuln['CVE_ID'] ) && '' !== $wpvuln['CVE_ID'] ) {
// @i8n below
?>
<p><span class="nvdlink">More details: <a href="<?php
echo esc_url( 'https://nvd.nist.gov/vuln/detail/' . $wpvuln['CVE_ID'] ) ;
?>" target="_blank" rel="noopener">Read more about <?php
echo esc_html( $wpvuln['CVE_ID'] ) ;
?></a></span></p>
<?php
}
?>
</div>
</div>
</div>
<?php
}
}
?>
</div><!-- .vuln vulnwordpress -->
<?php
}
// display list of vulns in plugins
if ( isset( $vulnerabilities['plugins'] ) ) {
// @i8n below
?>
<p><?php
_e( 'You should upgrade to latest version or find a different plugin as soon as possible.', 'security-ninja' );
?></p>
<?php
foreach ( $vulnerabilities['plugins'] as $key => $found_vuln ) {
?>
<div class="card vulnplugin">
<h3><span class="dashicons dashicons-warning"></span> Plugin: <?php
echo esc_html( $found_vuln['name'] ) ;
?> <span class="ver">v. <?php
echo esc_html( $found_vuln['installedVersion'] ) ;
?></span></h3>
<?php
if ( isset( $found_vuln['versionEndExcluding'] ) ) {
$searchurl = admin_url( 'plugins.php?s=' . rawurlencode( $found_vuln['name'] ) . '&plugin_status=all' );
?>
<div class="vulnrecommendation">
<p><?php
$searchurl = filter_var( $searchurl, FILTER_SANITIZE_URL );
printf(
// translators: Recommendation to update particular plugin to specific version
__( '<a href="%1$s">Update %2$s to minimum version %3$s</a>', 'security-ninja' ),
$searchurl,
esc_html( $found_vuln['name'] ),
esc_html( $found_vuln['versionEndExcluding'] )
);
?></p>
</div>
<?php
} elseif ( isset( $found_vuln['recommendation'] ) && '' !== $found_vuln['recommendation'] ) {
?>
<div class="vulnrecommendation">
<p><strong><?php
echo esc_html( $found_vuln['recommendation'] ) ;
?></strong></p>
</div>
<?php
}
if ( isset( $found_vuln['desc'] ) || isset( $found_vuln['refs'] ) ) {
?>
<div class="wrap-collabsible">
<input id="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="toggle" type="checkbox">
<label for="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="lbl-toggle">Details</label>
<div class="collapsible-content">
<div class="content-inner">
<?php
if ( isset( $found_vuln['desc'] ) && '' !== $found_vuln['desc'] ) {
?>
<p class="vulndesc"><?php
echo esc_html( $found_vuln['desc'] ) ;
?></p>
<?php
}
if ( isset( $found_vuln['refs'] ) && '' !== $found_vuln['refs'] ) {
$refs = json_decode( $found_vuln['refs'] );
if ( is_array( $refs ) ) {
?>
<h4><?php
_e( 'Read more:', 'security-ninja' );
?></h4>
<ul>
<?php
if ( isset( $found_vuln['CVE_ID'] ) && '' !== $found_vuln['CVE_ID'] ) {
?>
<li><a href="<?php
echo esc_url( 'https://nvd.nist.gov/vuln/detail/' . $found_vuln['CVE_ID'] ) ;
?>" target="_blank" class="exlink" rel="noopener"><?php
echo esc_attr( $found_vuln['CVE_ID'] ) ;
?></a></li>
<?php
}
foreach ( $refs as $ref ) {
?>
<li><a href="<?php
echo esc_url( $ref->url ) ;
?>" target="_blank" class="exlink" rel="noopener"><?php
echo esc_html( self::remove_http( $ref->name ) ) ;
?></a></li>
<?php
}
?>
</ul>
<?php
}
}
?>
</div>
</div>
</div>
<?php
}
?>
</div><!-- .vuln .vulnplugin -->
<?php
}
}
// end plugins
// display list of vulns in themes
if ( isset( $vulnerabilities['themes'] ) ) {
// @i8n below
?>
<p><?php
_e( 'Warning - Vulnerable themes found! Note: comparison is made by folder name. Please verify the theme before deleting.', 'security-ninja' );
?></p>
<?php
foreach ( $vulnerabilities['themes'] as $key => $found_vuln ) {
?>
<div class="card vulnplugin">
<h3><span class="dashicons dashicons-warning"></span> Theme: <?php
echo esc_html( $found_vuln['name'] ) ;
?> <span class="ver">v. <?php
echo esc_html( $found_vuln['installedVersion'] ) ;
?></span></h3>
<?php
if ( isset( $found_vuln['versionEndExcluding'] ) ) {
$searchurl = admin_url( 'plugins.php?s=' . rawurlencode( $found_vuln['name'] ) . '&plugin_status=all' );
?>
<div class="vulnrecommendation">
<p><?php
$searchurl = filter_var( $searchurl, FILTER_SANITIZE_URL );
printf(
// translators: Recommendation to update particular plugin to specific version
__( '<a href="%1$s">Update %2$s to minimum version %3$s</a>', 'security-ninja' ),
$searchurl,
esc_html( $found_vuln['name'] ),
esc_html( $found_vuln['versionEndExcluding'] )
);
?></p>
</div>
<?php
} elseif ( isset( $found_vuln['recommendation'] ) && '' !== $found_vuln['recommendation'] ) {
?>
<div class="vulnrecommendation">
<p><strong><?php
echo esc_html( $found_vuln['recommendation'] ) ;
?></strong></p>
</div>
<?php
}
if ( isset( $found_vuln['desc'] ) || isset( $found_vuln['refs'] ) ) {
?>
<div class="wrap-collabsible">
<input id="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="toggle" type="checkbox">
<label for="collapsible-<?php
echo esc_attr( $key ) ;
?>" class="lbl-toggle"><?php
_e( 'Details', 'security-ninja' );
?></label>
<div class="collapsible-content">
<div class="content-inner">
<?php
if ( isset( $found_vuln['desc'] ) && '' !== $found_vuln['desc'] ) {
?>
<p class="vulndesc"><?php
echo esc_html( $found_vuln['desc'] ) ;
?></p>
<?php
}
?>
<?php
if ( isset( $found_vuln['refs'] ) && '' !== $found_vuln['refs'] ) {
$refs = json_decode( $found_vuln['refs'] );
if ( is_array( $refs ) ) {
?>
<h4><?php
_e( 'Read more', 'security-ninja' );
?>:</h4>
<ul>
<?php
if ( isset( $found_vuln['CVE_ID'] ) && '' !== $found_vuln['CVE_ID'] ) {
?>
<li><a href="<?php
echo esc_url( 'https://nvd.nist.gov/vuln/detail/' . $found_vuln['CVE_ID'] ) ;
?>" target="_blank" class="exlink" rel="noopener"><?php
echo esc_attr( $found_vuln['CVE_ID'] ) ;
?></a></li>
<?php
}
foreach ( $refs as $ref ) {
?>
<li><a href="<?php
echo esc_url( $ref->url ) ;
?>" target="_blank" class="exlink" rel="noopener"><?php
echo esc_html( self::remove_http( $ref->name ) ) ;
?></a></li>
<?php
}
?>
</ul>
<?php
}
}
?>
</div>
</div>
</div>
<?php
}
?>
</div><!-- .vuln .vulnplugin -->
<?php
}
}
// end themes
} else {
$options = self::get_options();
// No update if feature disabled
if ( !$options['enable_vulns'] ) {
?>
<h3><?php
_e( 'The vulnerability scanner is disabled', 'security-ninja' );
?></h3>
<?php
} else {
?>
<h3><?php
esc_html_e( 'Great, no known vulnerabilities found on your website', 'security-ninja' );
?></h3>
<?php
}
}
?>
</div>
<div class="card">
<form method="post" action="options.php">
<?php
settings_fields( WF_SN_VU_OPTIONS_NAME );
?>
<h3 class="ss_header"><?php
_e( 'Settings', 'security-ninja' );
?></h3>
<table class="form-table">
<tbody>
<tr valign="top">
<th scope="row"><label for="wf_sn_vu_settings_group_enable_vulns"><?php
_e( 'Vulnerability scanning', 'security-ninja' );
?></label></th>
<td class="sn-cf-options">
<?php
Wf_Sn::create_toggle_switch( WF_SN_VU_OPTIONS_NAME . '_enable_vulns', array(
'value' => 1,
'saved_value' => $options['enable_vulns'],
'option_key' => WF_SN_VU_OPTIONS_NAME . '[enable_vulns]',
) );
?>
<p class="description"><?php
_e( 'Checking for known vulnerabilites.', 'security-ninja' );
?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="wf_sn_vu_settings_group_enable_admin_notification"><?php
_e( 'Admin counter', 'security-ninja' );
?></label></th>
<td class="sn-cf-options">
<?php
Wf_Sn::create_toggle_switch( WF_SN_VU_OPTIONS_NAME . '_enable_admin_notification', array(
'saved_value' => $options['enable_admin_notification'],
'option_key' => WF_SN_VU_OPTIONS_NAME . '[enable_admin_notification]',
) );
?>
<p class="description"><?php
_e( 'Disable warning notice in admin pages.', 'security-ninja' );
?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="wf_sn_vu_settings_group_enable_email_notice"><?php
_e( 'Email warnings', 'security-ninja' );
?></label></th>
<td class="sn-cf-options">
<?php
Wf_Sn::create_toggle_switch( WF_SN_VU_OPTIONS_NAME . '_enable_email_notice', array(
'saved_value' => $options['enable_email_notice'],
'option_key' => WF_SN_VU_OPTIONS_NAME . '[enable_email_notice]',
) );
?>
<p class="description"><?php
_e( 'Enable email notifications. Only when one or more vulnerabilites are detected.', 'security-ninja' );
?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="input_id"><?php
_e( 'Email recipient', 'security-ninja' );
?></label></th>
<td>
<input name="<?php
echo esc_attr( WF_SN_VU_OPTIONS_NAME ) ;
?>[email_notice_recipient]" type="text" value="<?php
echo esc_attr( $options['email_notice_recipient'] ) ;
?>" class="regular-text" placeholder="">
<p class="description"><?php
_e( 'Who should get the warning? The system will send an email when a vulnerability is
detected. Maximum one email per day.', 'security-ninja' );
?></p>
</td>
</tr>
<tr>
<td colspan="2">
<p class="submit"><input type="submit" value="<?php
_e( 'Save Changes', 'security-ninja' );
?>" class="input-button button-primary" name="Submit" />
</td>
</tr>
</tbody>
</table>
</form>
</div><!-- .card -->
<?php
if ( $options['enable_vulns'] ) {
?>
<p>
<?php
printf(
// translators: Shows how many vulnerabilities
esc_html__( 'Vulnerability list contains %1$s known vulnerabilities.', 'security-ninja' ),
esc_html( number_format_i18n( $total_vulnerabilities ) )
);
?>
</p>
<?php
$upload_dir = wp_upload_dir();
$file_path = $upload_dir['basedir'] . '/security-ninja/vulns/';
?>
<p>Path: <code><?php
echo $file_path ;
?></code></p>
<?php
}
?>
</div>
<?php
}
/**
* Display warning if test were never run
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return void
*/
public static function admin_notice_vulnerabilities()
{
global $current_screen ;
// dont show on the wizard page
if ( strpos( $current_screen->id, 'security-ninja-wizard' ) !== false ) {
return false;
}
$notice = get_option( WF_SN_VU_VULNS_NOTICE );
$title = 'Vulnerability list updated';
if ( $notice ) {
$allowed_tags = wp_kses_allowed_html( 'post' );
// 'post' ?
?>
<div class="secnin-notice notice notice-success is-dismissible" id="sn_vulnerability_updated">
<h3><span class="dashicons dashicons-yes-alt"></span><?php
echo $title ;
?></h3>
<p><?php
echo wp_kses( $notice, $allowed_tags ) ;
?></p>
</div>
<?php
// lets delete till next time.
delete_option( WF_SN_VU_VULNS_NOTICE );
}
if ( !\PAnD::is_admin_notice_active( 'dismiss-vulnerabilities-notice-1' ) || wf_sn::is_plugin_page() ) {
return;
}
$tests = get_option( WF_SN_VU_RESULTS_KEY );
$found_plugin_vulnerabilities = self::return_vulnerabilities();
if ( $found_plugin_vulnerabilities ) {
$total = 0;
if ( isset( $found_plugin_vulnerabilities['plugins'] ) ) {
$total = $total + count( $found_plugin_vulnerabilities['plugins'] );
}
if ( isset( $found_plugin_vulnerabilities['wordpress'] ) ) {
$total = $total + count( $found_plugin_vulnerabilities['wordpress'] );
}
if ( isset( $found_plugin_vulnerabilities['themes'] ) ) {
$total = $total + count( $found_plugin_vulnerabilities['themes'] );
}
if ( 0 === $total ) {
return;
}
?>
<div data-dismissible="dismiss-vulnerabilities-notice-1" class="secnin-notice notice notice-error is-dismissible" id="sn_vulnerability_warning_dismiss">
<h3><span class="dashicons dashicons-warning"></span>
<?php
// translators: Shown if one or multiple vulnerabilities found
echo esc_html( sprintf( _n(
'You have %s known vulnerability on your website!',
'You have %s known vulnerabilities on your website!',
$total,
'security-ninja'
), number_format_i18n( $total ) ) ) ;
?>
</h3>
<p>
<?php
printf( 'Visit the <a href="%s">Vulnerabilities tab</a> for more details.', esc_url( admin_url( 'admin.php?page=wf-sn#sn_vuln' ) ) );
?>
- <a href="#" class="dismiss-this"><?php
esc_html_e( 'Dismiss warning for 24 hours.', 'security-ninja' );
?></a></p>
</div>
<?php
}
}
/**
* Plugin activation routines
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @access public static
* @return void
*/
public static function activate()
{
// Download the vulnerability list for the first time
self::update_vuln_list();
}
/**
* Sanitize settings on save
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Sunday, January 3rd, 2021.
* @access public static
* @param mixed $values values to sanitize
* @return mixed
*/
public static function sanitize_settings( $values )
{
if ( !is_array( $values ) ) {
$values = array();
}
$old_options['enable_vulns'] = 0;
$old_options['enable_outdated'] = 0;
$old_options['enable_admin_notification'] = 0;
$old_options['enable_email_notice'] = 0;
$old_options['email_notice_recipient'] = '';
foreach ( $values as $key => $value ) {
switch ( $key ) {
case 'enable_vulns':
case 'enable_outdated':
case 'enable_admin_notification':
case 'enable_email_notice':
$values[$key] = intval( $value );
break;
case 'email_notice_recipient':
$values[$key] = sanitize_text_field( $value );
break;
}
}
$return = array_merge( $old_options, $values );
// Delete the transient when saving
delete_transient( 'wf_sn_return_vulnerabilities' );
return $return;
}
/**
* Routines that run on deactivation
*
* @author Lars Koudal
* @since v0.0.1
* @version v1.0.0 Friday, January 1st, 2021.
* @version v1.0.1 Saturday, November 19th, 2022.
* @access public static
* @return void
*/
public static function deactivate()
{
$centraloptions = Wf_Sn::get_options();
if ( !isset( $centraloptions['remove_settings_deactivate'] ) ) {
return;
}
if ( $centraloptions['remove_settings_deactivate'] ) {
delete_option( WF_SN_VU_RESULTS_KEY );
delete_option( WF_SN_VU_VULNS );
delete_option( WF_SN_VU_OUTDATED );
delete_option( WF_SN_VU_OPTIONS_KEY );
delete_option( WF_SN_VU_VULNS_NOTICE );
delete_option( WF_SN_VU_LAST_EMAIL );
}
}
}
// setup environment when activated
register_activation_hook( WF_SN_BASE_FILE, array( __NAMESPACE__ . '\\Wf_Sn_Vu', 'activate' ) );
// hook everything up
add_action( 'plugins_loaded', array( __NAMESPACE__ . '\\Wf_Sn_Vu', 'init' ) );
// when deativated clean up
register_deactivation_hook( WF_SN_BASE_FILE, array( __NAMESPACE__ . '\\Wf_Sn_Vu', 'deactivate' ) );
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists