HEX
Server: Apache
System: Linux vps-3158868-x.dattaweb.com 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
User: emerlux (1185)
PHP: 8.3.1
Disabled: system, shell, exec, system_exec, shell_exec, mysql_pconnect, passthru, popen, proc_open, proc_close, proc_nice, proc_terminate, proc_get_status, escapeshellarg, escapeshellcmd, eval
Upload Files
File: /home/emerlux/public_html/wp-content/plugins/wpvulnerability/wpvulnerability-general.php
<?php
/**
 * General functions
 *
 * @package WPVulnerability
 *
 * @version 2.0.0
 */

defined( 'ABSPATH' ) || die( 'No script kiddies please!' );

/**
 * Clear the existing cache for the specified type.
 *
 * @since 2.0.0
 *
 * @param string $type The type of cache to clear (core, plugins, themes).
 * @return void
 */
function wpvulnerability_clear_cache( $type ) {
	if ( is_multisite() ) {
		delete_site_option( "wpvulnerability-{$type}" );
		delete_site_option( "wpvulnerability-{$type}-vulnerable" );
		delete_site_option( "wpvulnerability-{$type}-cache" );
	} else {
		delete_option( "wpvulnerability-{$type}" );
		delete_option( "wpvulnerability-{$type}-vulnerable" );
		delete_option( "wpvulnerability-{$type}-cache" );
	}
}

/**
 * Checks and validates user capabilities for managing vulnerability settings in a WordPress environment.
 *
 * This function verifies if the current user has the appropriate permissions to manage network settings
 * in a multisite installation or manage options in a single site installation. It ensures that only
 * Administrators in a single site and Super Administrators in multisite can access these settings.
 *
 * @since 3.0.0
 *
 * @return bool Returns true if the current user has the required capabilities, false otherwise.
 */
function wpvulnerability_capabilities() {
	// Check if the user is logged in.
	if ( ! is_user_logged_in() ) {
		return false;
	}

	// Check if in a Multisite environment.
	if ( is_multisite() && is_super_admin() && ( is_network_admin() || is_main_site() ) ) {
		return true;
	} elseif ( is_admin() && current_user_can( 'manage_options' ) ) {
			return true;
	}

	// Return false if the user does not have the required capabilities.
	return false;
}

/**
 * Checks if the `shell_exec` function can be used.
 *
 * This function verifies if the `shell_exec` function is not disabled in the server's
 * configuration and is able to execute a basic shell command. It also checks for
 * safe mode, which is relevant for older PHP versions before 5.4.
 *
 * @since 3.4.0
 *
 * @return bool True if `shell_exec` is available and working, false otherwise.
 */
function wpvulnerability_can_shell_exec() {
	// Check if `shell_exec` is disabled.
	if ( in_array( 'shell_exec', array_map( 'trim', explode( ',', ini_get( 'disable_functions' ) ) ), true ) ) {
		return false; // `shell_exec` is disabled.
	}

	// Try to execute a simple command to confirm functionality.
	$test = shell_exec( 'echo test' ); // phpcs:ignore

	// If the command execution failed or returned null, shell_exec is not working.
	return null !== $test; // Return true if the command was successful.
}

/**
 * Retrieve the cache expiration in hours.
 *
 * The value can be defined via the WPVULNERABILITY_CACHE_HOURS constant,
 * configured in the plugin settings, or falls back to 12 hours.
 *
 * @since 4.1.0
 *
 * @return int Cache duration in hours.
 */
function wpvulnerability_cache_hours() {
				$default = 12;

	if ( defined( 'WPVULNERABILITY_CACHE_HOURS' ) && WPVULNERABILITY_CACHE_HOURS !== $default ) {
					return (int) WPVULNERABILITY_CACHE_HOURS;
	}

				$settings = is_multisite() ? get_site_option( 'wpvulnerability-config', array() ) : get_option( 'wpvulnerability-config', array() );
	if ( isset( $settings['cache'] ) ) {
					$cache = (int) $settings['cache'];
		if ( in_array( $cache, array( 1, 6, 12, 24 ), true ) ) {
						return $cache;
		}
	}

				return $default;
}

/**
 * Normalize various truthy and falsy values into the expected 'y' or 'n' format.
 *
 * This helper ensures that configuration options stored as booleans or integers
 * in previous plugin versions are converted into the new string-based format.
 *
 * @since 4.1.1
 *
 * @param mixed $value Value to normalize.
 *
 * @return string Returns 'y' when enabled, 'n' otherwise.
 */
function wpvulnerability_normalize_yes_no( $value ) {
	if ( is_string( $value ) ) {
			$value = strtolower( trim( $value ) );
	}

		$truthy = array( 'y', 'yes', '1', 1, true, 'true', 'on' );

		return in_array( $value, $truthy, true ) ? 'y' : 'n';
}

/**
 * Determine if a stored yes/no value should be treated as enabled.
 *
 * @since 4.1.1
 *
 * @param mixed $value Value to evaluate.
 *
 * @return bool True when enabled, false otherwise.
 */
function wpvulnerability_is_yes( $value ) {
		return 'y' === wpvulnerability_normalize_yes_no( $value );
}

/**
 * Normalize the notification configuration array.
 *
 * @since 4.1.1
 *
 * @param mixed $notify Notification configuration values.
 *
 * @return array Normalized notification configuration containing 'email', 'slack', and 'teams'.
 */
function wpvulnerability_normalize_notify_settings( $notify ) {
		$defaults   = array(
			'email' => 'n',
			'slack' => 'n',
			'teams' => 'n',
		);
		$normalized = array();

		if ( is_array( $notify ) ) {
			foreach ( $notify as $channel => $value ) {
					$normalized[ $channel ] = wpvulnerability_normalize_yes_no( $value );
			}
		}

		return array_merge( $defaults, $normalized );
}

/**
 * Sanitize a version string.
 *
 * This function removes any leading or trailing whitespace from the version string
 * and strips out any non-alphanumeric characters except for hyphens, underscores, and dots.
 *
 * @version 2.0.0
 *
 * @param string $version The version string to sanitize.
 *
 * @return string The sanitized version string.
 */
function wpvulnerability_sanitize_version( $version ) {
	// Remove any leading or trailing whitespace.
	$version = trim( (string) $version );

	// Strip out any non-alphanumeric characters except for hyphens, underscores, and dots.
	$version = preg_replace( '/[^a-zA-Z0-9_\-.]+/', '', $version );

	return $version;
}

/**
 * Sanitize a version string and validate its format.
 *
 * This function sanitizes the input version string and checks it against a regular expression
 * to match the standard versioning format (major.minor[.patch[.build]]). It returns the matched version
 * if it conforms to the expected format; otherwise, it returns the original version.
 *
 * @since 3.5.0 Introduced.
 *
 * @param string $version The version string to sanitize and validate.
 * @return string The sanitized version string if it matches the standard format; otherwise, the original version string.
 */
function wpvulnerability_sanitize_and_validate_version( $version ) {
	// Sanitize the version string using the base sanitizer.
	$version = wpvulnerability_sanitize_version( $version );

	// Validate format (major.minor[.patch[.build]]) and sanitize.
	if ( preg_match( '/^\d+\.\d+(\.\d+){0,2}(\.\d+)?/', $version, $match ) ) {
		if ( isset( $match[0] ) ) {
			return trim( $match[0] );
		}
	}

	return $version;
}

/**
 * Detects the version of SQLite using the SQLite3 extension or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of SQLite in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_sqlite() {
	// Initialize the version variable.
	$version = null;

	// First method: use the SQLite3 extension of PHP.
	if ( class_exists( 'SQLite3' ) ) {
		$sqlite       = new SQLite3( ':memory:' ); // Create an in-memory SQLite database.
		$version_info = $sqlite->version();

		if ( isset( $version_info['versionString'] ) ) {
			$version = $version_info['versionString'];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		// Command to check SQLite version.
        $version_output = shell_exec( escapeshellcmd( 'sqlite3 --version' ) ); // phpcs:ignore

		if ( ! empty( $version_output ) && preg_match( '/(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/', $version_output, $matches ) ) {
			$version = $matches[1];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Return the sanitized and validated version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the version of Redis using the Redis extension or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of Redis in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_redis() {
	// Initialize the version variable.
	$version = null;

	// First method: use the Redis extension of PHP.
	if ( class_exists( 'Redis' ) ) {
		$redis = new Redis();
		try {

			$redis_info = $redis->info();

			if ( isset( $redis_info['redis_version'] ) ) {
				$version = $redis_info['redis_version'];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
					$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
				}
			}
		} catch ( RedisException $e ) {
			// There is the PHP extension, but no Redis.
			unset( $redis );
		}
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		// Command to check Redis version.
        $version_output = shell_exec( escapeshellcmd( 'redis-server --version' ) ); // phpcs:ignore

		if ( ! empty( $version_output ) && preg_match( '/redis-server\s+v=([\d.]+)/i', $version_output, $matches ) ) {
			$version = $matches[1];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Return the sanitized and validated version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the version of Memcached using the Memcached extension or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of Memcached in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_memcached() {
	// Initialize the version variable.
	$version = null;

	// First method: use the Memcached extension of PHP.
	if ( class_exists( 'Memcached' ) ) {
		$memcached = new Memcached();
		try {

			$version_info = $memcached->getVersion();

			if ( is_array( $version_info ) && isset( $version_info[0] ) ) {
				// The version is usually the first element in the returned array.
				$version = $version_info[0];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
					$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
				}
			}
		} catch ( MemcachedException $e ) {
			// There is the PHP extension, but no memcached.
			unset( $memcached );
		}
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		// Command to check Memcached version.
        $version_output = shell_exec( escapeshellcmd( 'memcached -h' ) ); // phpcs:ignore

		if ( ! empty( $version_output ) && preg_match( '/memcached\s+(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/i', $version_output, $matches ) ) {
			$version = $matches[1];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Return the sanitized and validated version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the PHP version using the phpversion function or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of PHP in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_php() {
	// Initialize the version variable.
	$version = null;

	// First method: use the PHP_VERSION constant.
	if ( defined( 'PHP_VERSION' ) ) {
		$version = PHP_VERSION;
	}

	// First method: use the phpversion function.
	if ( empty( $version ) && function_exists( 'phpversion' ) ) {
		$version = phpversion();
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		// Command to check PHP version.
        $version_output = shell_exec( escapeshellcmd( 'php -v' ) ); // phpcs:ignore

		if ( ! empty( $version_output ) && preg_match( '/PHP\s+(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/i', $version_output, $matches ) ) {
			$version = $matches[1];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Return the sanitized and validated PHP version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the version of cURL using the cURL extension or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of cURL in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_curl() {
	// Product name for consistency.
	$version = null;

	// First method: use the cURL extension of PHP.
	if ( function_exists( 'curl_version' ) ) {
		$curl_info = curl_version();
		$version   = isset( $curl_info['version'] ) ? $curl_info['version'] : null;
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		// Command to check cURL version.
        $version_output = shell_exec( escapeshellcmd( 'curl --version' ) ); // phpcs:ignore

		if ( ! empty( $version_output ) && preg_match( '/curl\s+(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/i', $version_output, $matches ) ) {
			$version = $matches[1];
			// Replace "-N" at the end with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
				$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
			}
		}
	}

	// Return the sanitized and validated version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the version of ImageMagick using the Imagick extension or system commands.
 *
 * @since 3.5.0 Introduced.
 *
 * @return string|null The version of ImageMagick in the format N.n.n, N.n, etc., or null if it cannot be detected.
 */
function wpvulnerability_detect_imagemagick() {
	// Product name to avoid repetition and facilitate future changes.
	$version = null;

	// First method: use the Imagick extension of PHP.
	if ( extension_loaded( 'imagick' ) && class_exists( 'Imagick' ) ) {
		$imagick      = new Imagick();
		$version_info = $imagick->getVersion();

		if ( isset( $version_info['versionString'] ) ) {
			// Extract the version using a regular expression.
			if ( preg_match( '/ImageMagick\s+(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/i', $version_info['versionString'], $matches ) ) {
				$version = $matches[1];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
					$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
				}
			}
		}
	}

	// Second method: use system commands if the first fails and shell_exec is available.
	if ( empty( $version ) && wpvulnerability_can_shell_exec() ) {
		$commands = array( 'magick -version', 'convert -version' );

		foreach ( $commands as $cmd ) {
			// Execute the command securely.
            $version_output = shell_exec( escapeshellcmd( $cmd ) ); // phpcs:ignore

			if ( ! empty( $version_output ) && preg_match( '/ImageMagick\s+(\d+\.\d+(?:\.\d+)?(?:-\d+)?)/i', $version_output, $matches ) ) {
				$version = $matches[1];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $version, $suffix_matches ) ) {
					$version = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $version );
				}
				break; // Exit the loop once the version is found.
			}
		}
	}

	// Return the version or null if it cannot be detected.
	return wpvulnerability_sanitize_and_validate_version( $version );
}

/**
 * Detects the web server software and version from the SERVER_SOFTWARE server variable.
 *
 * This function attempts to identify the web server software (e.g., Apache, nginx) and its version
 * based on the 'SERVER_SOFTWARE' environment variable provided by the server. It uses regular expressions
 * to parse the web server name and version. The function also sanitizes the detected version number
 * to a standard format (major.minor.patch).
 *
 * @since 3.2.0 Introduced.
 *
 * @return array Returns an associative array with three keys:
 *               'id' => A short, lowercase identifier for the web server (e.g., 'apache', 'nginx'),
 *               'name' => A more readable name for the web server (e.g., 'Apache HTTPD', 'nginx'),
 *               'version' => The detected version of the web server, sanitized to a standard format.
 */
function wpvulnerability_detect_webserver() {
	// Initialize an array to hold the web server information.
	$webserver = array(
		'id'      => null,
		'name'    => null,
		'version' => null,
	);

	// Check if the SERVER_SOFTWARE variable is set.
	if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
		// Trim and sanitize the server software string.
		$webserver_software = trim( wp_kses( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ), 'strip' ) );

		// Use regular expressions to extract the web server name and version.
		if ( preg_match( '/^(\w+)\/?([^\s]*)/', $webserver_software, $matches ) ) {
			$webserver['name']    = isset( $matches[1] ) ? trim( $matches[1] ) : null;
			$webserver['version'] = isset( $matches[2] ) ? trim( $matches[2] ) : null;

			// Replace "-N" at the end of the version with ".N" if present.
			if ( preg_match( '/-(\d+)$/', $webserver['version'], $suffix_matches ) ) {
				$webserver['version'] = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $webserver['version'] );
			}
		}
	}

	// Normalize and set the web server ID based on the detected name.
	if ( ! empty( $webserver['name'] ) ) {
		$webserver['id'] = strtolower( $webserver['name'] );
		switch ( $webserver['id'] ) {
			case 'httpd':
			case 'apache':
				$webserver['id']   = 'apache';
				$webserver['name'] = 'Apache HTTPD';
				break;
			case 'nginx':
				$webserver['id']   = 'nginx';
				$webserver['name'] = 'nginx';
				break;
			// Additional web servers can be added here.
		}
	}

	// If the version is not detected, try to get it from the OS.
	if ( empty( $webserver['version'] ) && wpvulnerability_can_shell_exec() ) {
		if ( 'apache' === $webserver['id'] ) {
            $apache_version = shell_exec( 'apache2 -v 2>&1' ) ?: shell_exec( 'httpd -v 2>&1' ); // phpcs:ignore
			if ( preg_match( '/Apache\/([\d.]+)/', $apache_version, $version_matches ) ) {
				$webserver['version'] = $version_matches[1];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $webserver['version'], $suffix_matches ) ) {
					$webserver['version'] = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $webserver['version'] );
				}
			}
		} elseif ( 'nginx' === $webserver['id'] ) {
            $nginx_version = shell_exec( 'nginx -v 2>&1' ); // phpcs:ignore
			if ( preg_match( '/nginx\/([\d.]+)/', $nginx_version, $version_matches ) ) {
				$webserver['version'] = $version_matches[1];
				// Replace "-N" at the end with ".N" if present.
				if ( preg_match( '/-(\d+)$/', $webserver['version'], $suffix_matches ) ) {
					$webserver['version'] = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $webserver['version'] );
				}
			} else {
                $angie_version = shell_exec( 'angie -v 2>&1' ); // phpcs:ignore
				if ( preg_match( '/angie\/([\d.]+)/', $angie_version, $version_matches ) ) {
					$webserver['version'] = $version_matches[1];
					// Replace "-N" at the end with ".N" if present.
					if ( preg_match( '/-(\d+)$/', $webserver['version'], $suffix_matches ) ) {
						$webserver['version'] = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $webserver['version'] );
					}
				}
			}
		}
	}

	// Sanitize and validate the web server version format.
	if ( ! empty( $webserver['version'] ) ) {
		// Sanitize the version number to ensure it's in a 'major.minor.patch' format.
		$webserver['version'] = wpvulnerability_sanitize_and_validate_version( $webserver['version'] );
	}

	// Return the detected web server information.
	return $webserver;
}

/**
 * Detects the SQL server software and version from the database server.
 *
 * This function identifies the SQL server software (e.g., MariaDB, MySQL) and its version
 * by querying the database using the 'SHOW VARIABLES' command. It parses the server name
 * and version using the results and sanitizes the detected version number to a standard format (major.minor.patch).
 *
 * @since 3.4.0
 *
 * @return array Returns an associative array with three keys:
 *               'id' => A short, lowercase identifier for the SQL server (e.g., 'mariadb', 'mysql'),
 *               'name' => A more readable name for the SQL server (e.g., 'MariaDB', 'MySQL'),
 *               'version' => The detected version of the SQL server, sanitized to a standard format.
 */
function wpvulnerability_detect_sqlserver() {
	// Initialize an array to hold the SQL server information.
	$sqlserver = array(
		'id'      => null,
		'name'    => null,
		'version' => null,
	);

	global $wpdb;

	// Query to get the database server type (version_comment).
    $database_results = $wpdb->get_results( $wpdb->prepare( 'SHOW VARIABLES LIKE %s', 'version_comment' ) ); // phpcs:ignore
	if ( $wpdb->last_error && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		do_action( 'wpdb_last_error', $wpdb->last_error );
	}

	// Process the results to determine the database type.
	if ( ! empty( $database_results ) && isset( $database_results[0]->Value ) ) {
		$possible_database = trim( $database_results[0]->Value );

		if ( false !== stripos( $possible_database, 'mariadb' ) ) {
			$sqlserver['id']   = 'mariadb';
			$sqlserver['name'] = 'MariaDB';
		} elseif ( false !== stripos( $possible_database, 'mysql' ) ) {
			$sqlserver['id']   = 'mysql';
			$sqlserver['name'] = 'MySQL';
		}
	}

	// Query to get the database server version.
    $version_results = $wpdb->get_results( $wpdb->prepare( 'SHOW VARIABLES LIKE %s', 'version' ) ); // phpcs:ignore
	if ( $wpdb->last_error && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		do_action( 'wpdb_last_error', $wpdb->last_error );
	}

	// Process the results to determine the database version.
	if ( ! empty( $version_results ) && isset( $version_results[0]->Value ) ) {
		$possible_version = trim( $version_results[0]->Value );

		// Sanitize and set the version based on the detected server type.
		if ( 'mariadb' === $sqlserver['id'] ) {
			// Handle MariaDB versions, considering the format changes and potential '-N' suffix.
			if ( preg_match( '/(\d+\.\d+\.\d+(?:-\d+)?)/', $possible_version, $match ) ||
				preg_match( '/(\d+\.\d+(?:-\d+)?)/', $possible_version, $match ) ) {
				$sqlserver['version'] = $match[1];
			}
		} elseif ( 'mysql' === $sqlserver['id'] ) {
			// Handle MySQL versions, considering potential '-N' suffix.
			if ( preg_match( '/(\d+\.\d+\.\d+(?:-\d+)?)/', $possible_version, $match ) ||
				preg_match( '/(\d+\.\d+(?:-\d+)?)/', $possible_version, $match ) ) {
				$sqlserver['version'] = $match[1];
			} else {
				// Fallback to the entire version string if regex doesn't match.
				$sqlserver['version'] = $possible_version;
			}
		}
	}

	// Replace "-N" at the end with ".N" if present.
	if ( ! empty( $sqlserver['version'] ) && preg_match( '/-(\d+)$/', $sqlserver['version'], $suffix_matches ) ) {
		$sqlserver['version'] = preg_replace( '/-(\d+)$/', '.' . $suffix_matches[1], $sqlserver['version'] );
	}

	// Sanitize and validate the version format.
	if ( ! empty( $sqlserver['version'] ) ) {
		$sqlserver['version'] = wpvulnerability_sanitize_and_validate_version( $sqlserver['version'] );
	}

	// Return the detected SQL server information.
	return $sqlserver;
}

/**
 * Returns a human-readable HTML entity for the given comparison operator.
 *
 * This function takes a comparison operator in string format and returns
 * its corresponding HTML entity for better readability in web contexts.
 *
 * @version 2.0.0
 *
 * @param string $op The operator string to prettify.
 *
 * @return string The pretty operator HTML string.
 */
function wpvulnerability_pretty_operator( $op ) {
	// Normalize the operator string to lowercase and trim whitespace.
	$op = trim( strtolower( $op ) );

	// Define an associative array mapping operators to their HTML entities.
	$operator_map = array(
		'lt' => '&lt;&nbsp;',  // Less than.
		'le' => '&le;&nbsp;',  // Less than or equal to.
		'gt' => '&gt;&nbsp;',  // Greater than.
		'ge' => '&ge;&nbsp;',  // Greater than or equal to.
		'eq' => '&equals;&nbsp;', // Equal to.
		'ne' => '&ne;&nbsp;',  // Not equal to.
	);

	// Return the corresponding HTML entity, or the original operator if not recognized.
	return isset( $operator_map[ $op ] ) ? $operator_map[ $op ] : $op;
}

/**
 * Returns a human-readable severity level.
 *
 * This function takes a severity string and returns a human-readable
 * severity level, localized for translation.
 *
 * @version 2.0.0
 *
 * @param string $severity The severity string to prettify.
 *
 * @return string The human-readable severity string.
 */
function wpvulnerability_severity( $severity ) {
	// Normalize the severity string to lowercase and trim whitespace.
	$severity = trim( strtolower( $severity ) );

	// Define an associative array mapping severity codes to their human-readable equivalents.
	$severity_map = array(
		'n' => __( 'None', 'wpvulnerability' ),      // No severity.
		'l' => __( 'Low', 'wpvulnerability' ),       // Low severity.
		'm' => __( 'Medium', 'wpvulnerability' ),    // Medium severity.
		'h' => __( 'High', 'wpvulnerability' ),      // High severity.
		'c' => __( 'Critical', 'wpvulnerability' ),   // Critical severity.
	);

	// Return the corresponding human-readable severity, or the original if not recognized.
	return isset( $severity_map[ $severity ] ) ? $severity_map[ $severity ] : $severity;
}

/**
 * Retrieves vulnerabilities information from the API.
 *
 * This function fetches vulnerability information based on the provided type and slug.
 * It supports caching to minimize API requests and improve performance.
 *
 * @version 2.0.0
 *
 * @param string $type  The type of vulnerability. Can be 'core', 'plugin', or 'theme'.
 * @param string $slug  The slug of the plugin or theme. For core vulnerabilities, it is the version string.
 * @param int    $cache Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|bool An array with the vulnerability information or false if there's an error.
 */
function wpvulnerability_get( $type, $slug = '', $cache = 1 ) {
	// Validate vulnerability type and normalize.
	$type        = strtolower( trim( $type ) );
	$valid_types = array( 'core', 'plugin', 'theme' );

	if ( ! in_array( $type, $valid_types, true ) ) {
		wp_die( 'Unknown vulnerability type sent.' );
	}

	// Validate slug for plugin or theme.
	if ( ( 'plugin' === $type || 'theme' === $type ) && empty( sanitize_title( $slug ) ) ) {
		return false;
	}

	// Validate slug for core.
	if ( 'core' === $type && ! wpvulnerability_sanitize_version( $slug ) ) {
		return false;
	}

	// Cache key.
	$key = 'wpvulnerability_' . $type . '_' . $slug;

	// Attempt to retrieve cached data.
	$vulnerability_data = $cache ? ( is_multisite() ? get_site_transient( $key ) : get_transient( $key ) ) : null;

	// If not cached, fetch updated data.
	if ( empty( $vulnerability_data ) ) {
		$url      = WPVULNERABILITY_API_HOST . $type . '/' . $slug . '/';
		$response = wp_remote_get( $url, array( 'timeout' => 2500 ) );

		if ( ! is_wp_error( $response ) ) {
			$body = wp_remote_retrieve_body( $response );

			// Cache the response data.
			if ( is_multisite() ) {
					set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
			} else {
					set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
			}

			$vulnerability_data = $body; // Use the fresh data.
		}
	}

	return json_decode( $vulnerability_data, true );
}

/**
 * Retrieve vulnerabilities for a specific version of WordPress Core.
 *
 * This function fetches vulnerability information for a given version of WordPress Core.
 * If no version is provided, it retrieves vulnerabilities for the currently installed version.
 * It supports caching to minimize API requests and improve performance.
 *
 * @since 2.0.0
 *
 * @param string|null $version The version number of WordPress Core. If null, retrieves for the installed version.
 * @param int         $cache   Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|false Array of vulnerabilities, or false on error.
 */
function wpvulnerability_get_core( $version = null, $cache = 1 ) {
	// Sanitize the version number.
	if ( ! wpvulnerability_sanitize_version( $version ) ) {
		$version = null; // Reset version if sanitization fails.
	}

	// If version number is null, retrieve for the installed version.
	if ( is_null( $version ) ) {
		$version = get_bloginfo( 'version' );
	}

	// Get vulnerabilities from the API.
	$response = wpvulnerability_get( 'core', $version, $cache );

	// Check for errors in the response.
	if ( ( isset( $response['error'] ) && $response['error'] ) || empty( $response['data']['vulnerability'] ) ) {
		return false;
	}

	// Process vulnerabilities and return as an array.
	$vulnerabilities = array();
	foreach ( $response['data']['vulnerability'] as $v ) {
		$vulnerabilities[] = array(
			'name'   => isset( $v['name'] ) ? wp_kses( (string) $v['name'], 'strip' ) : null,
			'link'   => isset( $v['link'] ) ? esc_url_raw( (string) $v['link'] ) : null,
			'source' => isset( $v['source'] ) ? $v['source'] : null,
			'impact' => isset( $v['impact'] ) ? $v['impact'] : null,
		);
	}

	return $vulnerabilities;
}

/**
 * Determines if a vulnerability applies to the specified version of the plugin.
 *
 * @since 3.5.0 Introduced.
 *
 * @param array  $v The vulnerability data.
 * @param string $version The version of the plugin.
 *
 * @return bool True if the vulnerability applies, false otherwise.
 */
function wpvulnerability_is_vulnerability_applicable( $v, $version ) {
	// Check if the vulnerability has minimum and maximum versions.
	if ( isset( $v['operator']['min_operator'], $v['operator']['max_operator'] ) ) {
		return version_compare( $version, $v['operator']['min_version'], $v['operator']['min_operator'] ) &&
				version_compare( $version, $v['operator']['max_version'], $v['operator']['max_operator'] );
	}

	// Check if the vulnerability has only a maximum version.
	if ( isset( $v['operator']['max_operator'] ) ) {
		return version_compare( $version, $v['operator']['max_version'], $v['operator']['max_operator'] );
	}

	// Check if the vulnerability has only a minimum version.
	if ( isset( $v['operator']['min_operator'] ) ) {
		return version_compare( $version, $v['operator']['min_version'], $v['operator']['min_operator'] );
	}

	return false;
}

/**
 * Retrieves vulnerabilities for a specified plugin, optionally returning general plugin data.
 *
 * This function sanitizes the plugin slug and verifies the version number before querying the vulnerability API.
 * If `$data` is set to 1, it returns general information about the plugin instead of vulnerabilities.
 * The function returns an array of vulnerabilities or plugin data based on the `$data` parameter, or `false`
 * if no vulnerabilities are found or the version number is invalid and `$data` is not set.
 *
 * @since 2.0.0 Introduced.
 *
 * @param string $slug    The slug of the plugin to check for vulnerabilities.
 * @param string $version The version of the plugin to check. The function may return `false` if this is invalid and `$data` is not set.
 * @param int    $data    Optional. Set to 1 to return general plugin data instead of vulnerabilities. Default 0 (return vulnerabilities).
 * @param int    $cache   Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|false An array of vulnerabilities or plugin data if `$data` is set to 1, or `false` if no vulnerabilities are found or the version number is invalid and `$data` is not set.
 */
function wpvulnerability_get_plugin( $slug, $version, $data = 0, $cache = 1 ) {
	// Sanitize the plugin slug.
	$slug = sanitize_title( $slug );

	// If the version number is invalid, return false unless $data is set.
	if ( ! wpvulnerability_sanitize_version( $version ) && ! $data ) {
		return false;
	}

	// Get the response from the vulnerability API.
	$response = wpvulnerability_get( 'plugin', $slug, $cache );

	// If $data is set to 1, return general plugin data.
	if ( 1 === $data && isset( $response['data'] ) ) {
		return array(
			'name'   => wp_kses( (string) $response['data']['name'], 'strip' ),
			'link'   => esc_url( (string) $response['data']['link'] ),
			'latest' => number_format( (int) $response['data']['latest'], 0, '.', '' ),
			'closed' => number_format( (int) $response['data']['closed'], 0, '.', '' ),
		);
	}

	// Check for errors or empty vulnerability data.
	if ( ( isset( $response['error'] ) && $response['error'] ) || empty( $response['data']['vulnerability'] ) ) {
		return false;
	}

	// Create an empty array to store vulnerabilities.
	$vulnerabilities = array();

	// Loop through each vulnerability.
	foreach ( $response['data']['vulnerability'] as $v ) {
		// Check version constraints and add vulnerabilities accordingly.
		if ( wpvulnerability_is_vulnerability_applicable( $v, $version ) ) {
			$vulnerabilities[] = array(
				'name'        => wp_kses( (string) $v['name'], 'strip' ),
				'description' => wp_kses_post( (string) $v['description'] ),
				'versions'    => wp_kses(
					wpvulnerability_pretty_operator( isset( $v['operator']['min_operator'] ) ? $v['operator']['min_operator'] : '' ) .
					( isset( $v['operator']['min_version'] ) ? $v['operator']['min_version'] : '' ) . ' - ' .
					wpvulnerability_pretty_operator( isset( $v['operator']['max_operator'] ) ? $v['operator']['max_operator'] : '' ) .
					( isset( $v['operator']['max_version'] ) ? $v['operator']['max_version'] : '' ),
					'strip'
				),
				'version'     => wp_kses( (string) ( isset( $v['operator']['min_version'] ) ? $v['operator']['min_version'] : $v['operator']['max_version'] ), 'strip' ),
				'unfixed'     => (int) ( isset( $v['operator']['unfixed'] ) ? $v['operator']['unfixed'] : 0 ),
				'closed'      => (int) ( isset( $v['operator']['closed'] ) ? $v['operator']['closed'] : 0 ),
				'source'      => isset( $v['source'] ) ? $v['source'] : null,
				'impact'      => isset( $v['impact'] ) ? $v['impact'] : null,
			);
		}
	}

	return $vulnerabilities;
}

/**
 * Get vulnerabilities for a specific theme.
 *
 * This function retrieves and sanitizes the theme slug and version before querying the vulnerability API.
 * It returns an array of vulnerabilities if any are found, or false if there are none.
 *
 * @since 3.5.0
 *
 * @param string $slug    Slug of the theme.
 * @param string $version Version of the theme.
 * @param int    $cache   Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|false Returns an array of vulnerabilities, or false if there are none.
 */
function wpvulnerability_get_theme( $slug, $version, $cache = 1 ) {
	// Sanitize the theme slug.
	$slug = sanitize_title( $slug );

	// Validate the version number.
	if ( ! wpvulnerability_sanitize_version( $version ) ) {
		return false; // Return false if the version is invalid.
	}

	// Get the response from the vulnerability API.
	$response = wpvulnerability_get( 'theme', $slug, $cache );

	// Check for errors or empty vulnerability data.
	if ( ( isset( $response['error'] ) && $response['error'] ) || empty( $response['data']['vulnerability'] ) ) {
		return false;
	}

	// Process each vulnerability.
	$vulnerabilities = array();
	foreach ( $response['data']['vulnerability'] as $v ) {
		// Check if the version falls within the min and max operator range.
		if ( wpvulnerability_is_vulnerability_applicable( $v, $version ) ) {
			$vulnerabilities[] = array(
				'name'        => wp_kses( (string) $v['name'], 'strip' ),
				'description' => wp_kses_post( (string) $v['description'] ),
				'versions'    => wp_kses(
					wpvulnerability_pretty_operator( isset( $v['operator']['min_operator'] ) ? $v['operator']['min_operator'] : '' ) .
					( isset( $v['operator']['min_version'] ) ? $v['operator']['min_version'] : '' ) . ' - ' .
					wpvulnerability_pretty_operator( isset( $v['operator']['max_operator'] ) ? $v['operator']['max_operator'] : '' ) .
					( isset( $v['operator']['max_version'] ) ? $v['operator']['max_version'] : '' ),
					'strip'
				),
				'version'     => wp_kses(
					(string) ( isset( $v['operator']['min_version'] ) ? $v['operator']['min_version'] : $v['operator']['max_version'] ),
					'strip'
				),
				'unfixed'     => (int) ( isset( $v['operator']['unfixed'] ) ? $v['operator']['unfixed'] : 0 ),
				'closed'      => (int) ( isset( $v['operator']['closed'] ) ? $v['operator']['closed'] : 0 ),
				'source'      => isset( $v['source'] ) ? $v['source'] : null,
				'impact'      => isset( $v['impact'] ) ? $v['impact'] : null,
			);
		}
	}

	return $vulnerabilities;
}

/**
 * Get statistics.
 *
 * Returns an array with statistical information about vulnerabilities and their respective products.
 *
 * @since 2.0.0
 *
 * @param int $cache Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|false Returns an array with the statistical information if successful, false otherwise.
 */
function wpvulnerability_get_statistics( $cache = 1 ) {
	$key = 'wpvulnerability_stats';

	// Attempt to get cached statistics.
	$vulnerability = $cache ? ( is_multisite() ? get_site_transient( $key ) : get_transient( $key ) ) : null;

	// If cached statistics are not available, retrieve them from the API.
	if ( empty( $vulnerability ) ) {
		$url      = WPVULNERABILITY_API_HOST;
		$response = wp_remote_get( $url, array( 'timeout' => 2500 ) );

		if ( ! is_wp_error( $response ) ) {
			$body = wp_remote_retrieve_body( $response );
			// Cache the response data.
			if ( is_multisite() ) {
							set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
			} else {
							set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
			}
			$vulnerability = $body; // Use the fresh data.
		}
	}

	// Decode the JSON response and check for statistics.
	$response = json_decode( $vulnerability, true );
	if ( ! isset( $response['stats'] ) ) {
		return false;
	}

	$sponsors = array();
	if ( isset( $response['behindtheproject']['sponsors'] ) && count( $response['behindtheproject']['sponsors'] ) ) {

		foreach ( $response['behindtheproject']['sponsors'] as $s ) {

			$sponsors[] = $s;

			unset( $s );
		}
	}

	$contributors = array();
	if ( isset( $response['behindtheproject']['contributors'] ) && count( $response['behindtheproject']['contributors'] ) ) {

		foreach ( $response['behindtheproject']['contributors'] as $s ) {

			$contributors[] = $s;

			unset( $s );
		}
	}

	// Return an array with statistical information.
	return array(
		'core'         => array(
			'versions' => (int) $response['stats']['products']['core'],
		),
		'plugins'      => array(
			'products'        => (int) $response['stats']['products']['plugins'],
			'vulnerabilities' => (int) $response['stats']['plugins'],
		),
		'themes'       => array(
			'products'        => (int) $response['stats']['products']['themes'],
			'vulnerabilities' => (int) $response['stats']['themes'],
		),
		'php'          => array(
			'vulnerabilities' => (int) $response['stats']['php'],
		),
		'apache'       => array(
			'vulnerabilities' => (int) $response['stats']['apache'],
		),
		'nginx'        => array(
			'vulnerabilities' => (int) $response['stats']['nginx'],
		),
		'mariadb'      => array(
			'vulnerabilities' => (int) $response['stats']['mariadb'],
		),
		'mysql'        => array(
			'vulnerabilities' => (int) $response['stats']['mysql'],
		),
		'imagemagick'  => array(
			'vulnerabilities' => (int) $response['stats']['imagemagick'],
		),
		'curl'         => array(
			'vulnerabilities' => (int) $response['stats']['curl'],
		),
		'memcached'    => array(
			'vulnerabilities' => (int) $response['stats']['memcached'],
		),
		'redis'        => array(
			'vulnerabilities' => (int) $response['stats']['redis'],
		),
		'sqlite'       => array(
			'vulnerabilities' => (int) $response['stats']['sqlite'],
		),
		'sponsors'     => $sponsors,
		'contributors' => $contributors,
		'updated'      => array(
			'unixepoch' => (int) $response['updated'],
			'datetime'  => gmdate( 'Y-m-d H:i:s', (int) $response['updated'] ),
			'iso8601'   => gmdate( 'c', (int) $response['updated'] ),
			'rfc2822'   => gmdate( 'r', (int) $response['updated'] ),
		),
	);
}

/**
 * Retrieves the latest vulnerability statistics.
 *
 * This function calls the wpvulnerability API to get fresh statistics related to vulnerabilities
 * and returns the updated information.
 *
 * @since 3.4.0
 *
 * @return array|false The updated vulnerability statistics, or false on error.
 */
function wpvulnerability_get_fresh_statistics() {
	// Call the function to get the latest vulnerability statistics.
	$statistics_api_response = wpvulnerability_get_statistics();

	// Return the response from the API.
	return $statistics_api_response;
}

/**
 * Retrieves and caches the latest vulnerability statistics.
 *
 * This function retrieves the most recent vulnerability statistics, caches the data,
 * and returns the information as a JSON-encoded array. The cache expiration timestamp is also updated.
 *
 * @since 3.4.0
 *
 * @return string JSON-encoded array containing the vulnerability statistics.
 */
function wpvulnerability_statistics_get() {
	// Retrieve fresh statistics.
	$statistics = wpvulnerability_get_fresh_statistics();

	// Cache the statistics data and the timestamp for cache expiration.
	$encoded_statistics   = wp_json_encode( $statistics );
		$cache_expiration = number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' );

	if ( is_multisite() ) {
		update_site_option( 'wpvulnerability-statistics', $encoded_statistics );
		update_site_option( 'wpvulnerability-statistics-cache', $cache_expiration );
	} else {
		update_option( 'wpvulnerability-statistics', $encoded_statistics );
		update_option( 'wpvulnerability-statistics-cache', $cache_expiration );
	}

	// Return the JSON-encoded array of statistics data.
	return $encoded_statistics;
}

/**
 * Get vulnerabilities for a specific product version.
 *
 * This function retrieves vulnerability data for a specified product version.
 * It supports caching to minimize API requests and improve performance.
 *
 * @since 3.5.0
 *
 * @param string $type    The type of product (e.g., 'php', 'apache', 'nginx', 'mariadb', 'mysql').
 * @param string $version The version of the product to check.
 * @param int    $cache   Optional. Whether to use cache. Default is 1 (true).
 *
 * @return array|false Returns an array of vulnerabilities, or false if there are none.
 */
function wpvulnerability_get_vulnerabilities( $type, $version, $cache = 1 ) {
	$key                = 'wpvulnerability_' . $type;
	$vulnerability_data = null;
	$vulnerability      = array();

	// Get cached statistics if available.
	if ( $cache ) {
		$vulnerability_data = is_multisite() ? get_site_transient( $key ) : get_transient( $key );
	}

	// If cached statistics are not available, retrieve them from the API and store them in cache.
	if ( empty( $vulnerability_data ) ) {
		$url      = WPVULNERABILITY_API_HOST . $type . '/' . $version . '/';
		$response = wp_remote_get( $url, array( 'timeout' => 2500 ) );

		if ( ! is_wp_error( $response ) ) {
			$body = wp_remote_retrieve_body( $response );
			if ( $cache ) {
				if ( is_multisite() ) {
						set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
				} else {
						set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
				}
			}
			$vulnerability_data = $body; // Use the fresh data.
		}
	}

	// If the response does not contain vulnerabilities, return false.
	$response = json_decode( $vulnerability_data, true );

	if ( ( isset( $response['error'] ) && $response['error'] ) || empty( $response['data']['vulnerability'] ) ) {
		return false;
	}

	// Process each vulnerability.
	foreach ( $response['data']['vulnerability'] as $v ) {
		// Check if the version falls within the specified min and max operator range.
		if ( isset( $v['operator']['min_operator'], $v['operator']['max_operator'] ) &&
		     ! empty( $v['operator']['min_operator'] ) && ! empty( $v['operator']['max_operator'] ) ) {
			if ( version_compare( $version, $v['operator']['min_version'], $v['operator']['min_operator'] ) &&
				version_compare( $version, $v['operator']['max_version'], $v['operator']['max_operator'] ) ) {
				$vulnerability[] = array(
					'name'     => wp_kses( (string) $v['name'], 'strip' ),
					'versions' => wp_kses( wpvulnerability_pretty_operator( $v['operator']['min_operator'] ) . $v['operator']['min_version'] . ' - ' . wpvulnerability_pretty_operator( $v['operator']['max_operator'] ) . $v['operator']['max_version'], 'strip' ),
					'version'  => wp_kses( (string) $v['operator']['min_version'], 'strip' ),
					'unfixed'  => (int) $v['operator']['unfixed'],
					'source'   => $v['source'],
				);
			}
		} elseif ( isset( $v['operator']['max_operator'] ) ) {
			if ( version_compare( $version, $v['operator']['max_version'], $v['operator']['max_operator'] ) ) {
				$vulnerability[] = array(
					'name'     => wp_kses( (string) $v['name'], 'strip' ),
					'versions' => wp_kses( wpvulnerability_pretty_operator( $v['operator']['max_operator'] ) . $v['operator']['max_version'], 'strip' ),
					'version'  => wp_kses( (string) $v['operator']['max_version'], 'strip' ),
					'unfixed'  => (int) $v['operator']['unfixed'],
					'source'   => $v['source'],
				);
			}
		} elseif ( isset( $v['operator']['min_operator'] ) ) {
			if ( version_compare( $version, $v['operator']['min_version'], $v['operator']['min_operator'] ) ) {
				$vulnerability[] = array(
					'name'     => wp_kses( (string) $v['name'], 'strip' ),
					'versions' => wp_kses( wpvulnerability_pretty_operator( $v['operator']['min_operator'] ) . $v['operator']['min_version'], 'strip' ),
					'version'  => wp_kses( (string) $v['operator']['min_version'], 'strip' ),
					'unfixed'  => (int) $v['operator']['unfixed'],
					'source'   => $v['source'],
				);
			}
		}
	}

	return $vulnerability;
}