277 lines
8.9 KiB
PHP
277 lines
8.9 KiB
PHP
<?php
|
|
/**
|
|
* Software functions
|
|
*
|
|
* @package WPVulnerability
|
|
*
|
|
* @version 3.5.0
|
|
*/
|
|
|
|
defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
|
|
|
|
/**
|
|
* Retrieves the specified software version.
|
|
*
|
|
* This function returns the version of PHP, Apache, Nginx, MySQL, or MariaDB
|
|
* after performing necessary validations. It ensures that the returned value
|
|
* is clean and sanitized.
|
|
*
|
|
* @since 3.5.0
|
|
* @since 4.3.0 Updated to handle hybrid detection format from ImageMagick, Redis, Memcached, and SQLite.
|
|
*
|
|
* @param string $software The name of the software ('php', 'apache', 'nginx', 'mysql', 'mariadb').
|
|
* @return string|null The sanitized version of the software, or null if not found.
|
|
*/
|
|
function wpvulnerability_get_software_version( $software ) {
|
|
switch ( $software ) {
|
|
case 'php':
|
|
$php_version = wpvulnerability_detect_php();
|
|
if ( null !== $php_version && '' !== $php_version ) {
|
|
return wp_kses( (string) $php_version, 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'apache':
|
|
case 'nginx':
|
|
$webserver = wpvulnerability_detect_webserver();
|
|
if ( isset( $webserver['id'] ) && $webserver['id'] === $software && ! empty( $webserver['version'] ) ) {
|
|
return wp_kses( (string) $webserver['version'], 'strip' );
|
|
}
|
|
break;
|
|
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
$sqlserver = wpvulnerability_detect_sqlserver();
|
|
if ( isset( $sqlserver['id'] ) && $sqlserver['id'] === $software && ! empty( $sqlserver['version'] ) ) {
|
|
return wp_kses( (string) $sqlserver['version'], 'strip' );
|
|
}
|
|
break;
|
|
|
|
case 'imagemagick':
|
|
$detection = wpvulnerability_detect_imagemagick();
|
|
if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
|
|
return wp_kses( (string) $detection['version'], 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'curl':
|
|
$curl_version = wpvulnerability_detect_curl();
|
|
if ( null !== $curl_version && '' !== $curl_version ) {
|
|
return wp_kses( (string) $curl_version, 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'memcached':
|
|
$detection = wpvulnerability_detect_memcached();
|
|
if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
|
|
return wp_kses( (string) $detection['version'], 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'redis':
|
|
$detection = wpvulnerability_detect_redis();
|
|
if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
|
|
return wp_kses( (string) $detection['version'], 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'sqlite':
|
|
$detection = wpvulnerability_detect_sqlite();
|
|
if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
|
|
return wp_kses( (string) $detection['version'], 'strip' );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieves vulnerabilities for a given software version and updates its data.
|
|
*
|
|
* This function detects the installed software version, checks for vulnerabilities using an external API,
|
|
* and updates the data array with the vulnerabilities found.
|
|
*
|
|
* @since 3.5.0
|
|
*
|
|
* @param string $software The software name (e.g., 'php', 'apache', 'nginx', 'mysql', 'mariadb').
|
|
*
|
|
* @return array<string, mixed> The updated data array containing vulnerability information.
|
|
*/
|
|
function wpvulnerability_get_fresh_vulnerabilities( $software ) {
|
|
|
|
$version = null;
|
|
$data = array(
|
|
'vulnerabilities' => null,
|
|
'vulnerable' => 0,
|
|
'lifecycle' => array(),
|
|
);
|
|
|
|
switch ( $software ) {
|
|
case 'php':
|
|
case 'apache':
|
|
case 'nginx':
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
case 'imagemagick':
|
|
case 'curl':
|
|
case 'memcached':
|
|
case 'redis':
|
|
case 'sqlite':
|
|
$version = wpvulnerability_get_software_version( $software );
|
|
break;
|
|
|
|
default:
|
|
return $data;
|
|
}
|
|
|
|
if ( $version ) {
|
|
$transient_key = 'wpvulnerability_' . $software;
|
|
|
|
// Delete the transient so the next call fetches fresh data and repopulates it.
|
|
if ( is_multisite() ) {
|
|
delete_site_transient( $transient_key );
|
|
} else {
|
|
delete_transient( $transient_key );
|
|
}
|
|
|
|
switch ( $software ) {
|
|
case 'php':
|
|
case 'apache':
|
|
case 'nginx':
|
|
case 'mysql':
|
|
case 'mariadb':
|
|
case 'imagemagick':
|
|
case 'curl':
|
|
case 'memcached':
|
|
case 'redis':
|
|
case 'sqlite':
|
|
// cache=1: transient was just cleared, so a fresh API call is made and result cached.
|
|
$api_response = wpvulnerability_get_vulnerabilities( $software, $version, 1 );
|
|
break;
|
|
}
|
|
|
|
if ( ! empty( $api_response ) ) {
|
|
$data['vulnerabilities'] = $api_response;
|
|
$data['vulnerable'] = 1;
|
|
}
|
|
|
|
// Read back the transient to extract lifecycle fields from the full API response.
|
|
$raw_body = is_multisite() ? get_site_transient( $transient_key ) : get_transient( $transient_key );
|
|
$raw_response = json_decode( is_string( $raw_body ) ? $raw_body : '', true );
|
|
if ( is_array( $raw_response ) && isset( $raw_response['data'] ) && is_array( $raw_response['data'] ) ) {
|
|
$resp_data = $raw_response['data'];
|
|
$data['lifecycle'] = array(
|
|
'name' => is_scalar( $resp_data['name'] ?? '' ) ? (string) ( $resp_data['name'] ?? '' ) : '',
|
|
'status' => is_scalar( $resp_data['status'] ?? '' ) ? (string) ( $resp_data['status'] ?? '' ) : '',
|
|
'date_start' => is_scalar( $resp_data['date_start'] ?? '' ) ? (string) ( $resp_data['date_start'] ?? '' ) : '',
|
|
'date_end' => is_scalar( $resp_data['date_end'] ?? '' ) ? (string) ( $resp_data['date_end'] ?? '' ) : '',
|
|
);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get Installed Software
|
|
*
|
|
* Retrieves the list of installed software versions, checks for vulnerabilities,
|
|
* caches the data, and sends an email notification if vulnerabilities are detected.
|
|
*
|
|
* @since 3.5.0
|
|
*
|
|
* @param string $software The software name (e.g., 'php', 'apache').
|
|
*
|
|
* @return string JSON-encoded array of software data with vulnerabilities and vulnerable status.
|
|
*/
|
|
function wpvulnerability_get_installed( $software ) {
|
|
|
|
$wpvulnerability_software_vulnerable = 0;
|
|
|
|
// Retrieve fresh vulnerabilities for the installed software version.
|
|
$data = wpvulnerability_get_fresh_vulnerabilities( $software );
|
|
|
|
// Check if the software version is vulnerable and count the vulnerabilities.
|
|
if ( isset( $data['vulnerable'] ) && is_numeric( $data['vulnerable'] ) && (int) $data['vulnerable'] ) {
|
|
$vulns = isset( $data['vulnerabilities'] ) && is_array( $data['vulnerabilities'] ) ? $data['vulnerabilities'] : array();
|
|
$wpvulnerability_software_vulnerable = count( $vulns );
|
|
}
|
|
|
|
// Cache the vulnerability data and the timestamp for cache expiration.
|
|
if ( is_multisite() ) {
|
|
update_site_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
|
|
update_site_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
|
|
update_site_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
|
|
} else {
|
|
update_option( 'wpvulnerability-' . $software, wp_json_encode( $data ), false );
|
|
update_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ), false );
|
|
update_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ), false );
|
|
}
|
|
|
|
// Return the JSON-encoded array of software data.
|
|
$encoded = wp_json_encode( $data );
|
|
return false !== $encoded ? $encoded : '[]';
|
|
}
|
|
|
|
/**
|
|
* Get cached software vulnerabilities without triggering remote calls.
|
|
*
|
|
* @since 3.5.0
|
|
*
|
|
* @param string $software The software name (e.g., 'php', 'apache').
|
|
*
|
|
* @return array<string, mixed>|null Array of software data with vulnerabilities, or null if software is invalid.
|
|
*/
|
|
function wpvulnerability_software_get_vulnerabilities( $software ) {
|
|
|
|
$valid_software = array( 'php', 'apache', 'mariadb', 'mysql', 'nginx', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
|
|
|
|
// Use strict comparison for in_array.
|
|
if ( in_array( $software, $valid_software, true ) ) {
|
|
if ( is_multisite() ) {
|
|
$raw = get_site_option( 'wpvulnerability-' . $software );
|
|
$data = json_decode( is_string( $raw ) ? $raw : '', true );
|
|
} else {
|
|
$raw = get_option( 'wpvulnerability-' . $software );
|
|
$data = json_decode( is_string( $raw ) ? $raw : '', true );
|
|
}
|
|
|
|
return is_array( $data ) ? $data : array();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the software cache and remove any old cache data.
|
|
*
|
|
* @since 3.0.0
|
|
*
|
|
* @param string $software The software name (e.g., 'php', 'apache').
|
|
*
|
|
* @return void
|
|
*/
|
|
function wpvulnerability_get_vulnerabilities_clean( $software ) {
|
|
|
|
// Skip detection for components the administrator has hidden (either via the
|
|
// analysis settings or a WPVULNERABILITY_HIDE_* wp-config constant). This
|
|
// prevents shell_exec from running for components that are meant to be
|
|
// deactivated, matching the behaviour of the core/plugins/themes clean functions.
|
|
if ( ! wpvulnerability_analyze_filter( $software ) ) {
|
|
return;
|
|
}
|
|
|
|
// Update the installed software cache.
|
|
wpvulnerability_get_installed( $software );
|
|
}
|