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-themes.php
<?php
/**
 * Themes functions
 *
 * @package WPVulnerability
 *
 * @version 2.0.0
 */

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

/**
 * Adds a vulnerability notice under vulnerable themes.
 *
 * @since 2.0.0
 *
 * @param string $theme_file Main themes folder/file name.
 * @param array  $theme_data Theme data.
 *
 * @return void
 */
function wpvulnerability_theme_info_after( $theme_file, $theme_data ) {

	// Retrieve the vulnerabilities for all themes from the options table and decode the JSON.
	if ( is_multisite() ) {
		$theme_vulnerabilities = json_decode( get_site_option( 'wpvulnerability-themes' ), true );
	} else {
		$theme_vulnerabilities = json_decode( get_option( 'wpvulnerability-themes' ), true );
	}

	// Determine whether the theme is active and add an appropriate CSS class to the table row.
	$current_theme = wp_get_theme();
	$tr_class      = '';
	if ( $theme_file === $current_theme->get_stylesheet() ) {
		$tr_class .= 'active';
	}

	// Generate the vulnerability notice message with the theme name.
	$message = sprintf(
		/* translators: 1: theme name */
		__( '%1$s has a known vulnerability that may be affecting this version.', 'wpvulnerability' ),
		wp_kses( (string) $theme_data->get( 'Name' ), 'strip' )
	);

	// Begin generating the table row HTML markup with appropriate CSS classes and the vulnerability notice message.
	$information  = '<tr class="wpvulnerability ' . esc_attr( $tr_class ) . '">';
	$information .= '<td colspan="4">';
	$information .= '<p class="text-red"><img src="' . esc_url( WPVULNERABILITY_PLUGIN_URL ) . 'assets/logo16.png" style="height: 16px; vertical-align: text-top; width: 16px;" alt="" title="WPVulnerability"> <strong>' . esc_html( $message ) . '</strong></p>';
	$information .= '<table>';

	// Loop through all vulnerabilities for the current theme and add their details to the table row HTML markup.
	$vulnerabilities = $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerabilities'];

	foreach ( $vulnerabilities as $vulnerability ) {
		$what = array();
		if ( isset( $vulnerability['impact']['cwe'] ) ) {
			foreach ( $vulnerability['impact']['cwe'] as $vulnerability_cwe ) {
				$what[] = '<div><b>' . wp_kses( (string) $vulnerability_cwe['name'], 'strip' ) . '</b></div><div><i>' . wp_kses_post( (string) $vulnerability_cwe['description'] ) . '</i></div>';
			}
		}

		$sources = array();
		if ( isset( $vulnerability['source'] ) ) {
			foreach ( $vulnerability['source'] as $vulnerability_source ) {
				$sources[] = '<a href="' . esc_url_raw( (string) $vulnerability_source['link'], 'strip' ) . '" target="_blank" rel="external nofollow noopener noreferrer">[+]</a>&nbsp;' . wp_kses( (string) $vulnerability_source['name'], 'strip' );
			}
		}
		if ( count( $sources ) ) {
			$source = '<div style="padding-bottom: 5px;">' . implode( '<br>', $sources ) . '</div>';
		}

		$score = null;
		if ( isset( $vulnerability['impact']['cvss']['score'] ) ) {
			$score = number_format( (float) $vulnerability['impact']['cvss']['score'], 1, '.', '' );
		}
		$severity = null;
		if ( isset( $vulnerability['impact']['cvss']['severity'] ) ) {
			$severity = wpvulnerability_severity( $vulnerability['impact']['cvss']['severity'] );
		}

		$information .= '<tr>';
		$information .= '<td style="max-width: 256px; min-width: 96px;"><b>' . wp_kses( (string) $vulnerability['versions'], 'strip' ) . '</b></td>';
		$information .= '<td>';
		if ( (int) $vulnerability['closed'] || (int) $vulnerability['unfixed'] ) {
			$information .= '<div style="padding-bottom: 5px;">';
			if ( (int) $vulnerability['closed'] ) {
				$information .= '<div class="text-red">' . __( 'This theme is closed. Please replace it with another.', 'wpvulnerability' ) . '</div>';
			}
			if ( (int) $vulnerability['unfixed'] ) {
				$information .= '<div class="text-red">' . __( 'This vulnerability appears to be unpatched. Stay tuned for upcoming theme updates.', 'wpvulnerability' ) . '</div>';
			}
			$information .= '</div>';
		}
		if ( count( $what ) ) {
			$information .= '<div style="padding-bottom: 5px;">';
			foreach ( $what as $w ) {
				$information .= $w;
			}
			$information .= '</div>';
		}
		if ( ! is_null( $score ) || ! is_null( $severity ) ) {
			$information .= '<div style="padding-bottom: 5px;">';
			if ( ! is_null( $score ) ) {
				$information .= '<div>' . __( 'Global score: ', 'wpvulnerability' ) . $score . ' / 10</div>';
			}
			if ( ! is_null( $severity ) ) {
				$information .= '<div>' . __( 'Severity: ', 'wpvulnerability' ) . $severity . '</div>';
			}
			$information .= '</div>';
		}
		$information .= wp_kses( (string) $source, 'post' );
		$information .= '</td>';
		$information .= '</tr>';
	}

	$information .= '</table>';
	$information .= '</td>';
	$information .= '</tr>';

	echo $information; // phpcs:ignore
}

/**
 * Retrieves vulnerabilities for a given theme and updates its data.
 *
 * @since 2.0.0
 *
 * @param array  $theme_data The theme data array.
 * @param string $theme_slug The slug to the theme.
 *
 * @return array The updated theme data array.
 */
function wpvulnerability_get_fresh_theme_vulnerabilities( $theme_data, $theme_slug ) {

	// Get the theme version and slug from the theme data.
	$theme_version        = wp_kses( (string) $theme_data['data']->get( 'Version' ), 'strip' );
	$theme_data_v['slug'] = $theme_slug;

	// Initialize vulnerability related fields.
	$theme_data_v['name']            = isset( $theme_data['data'] ) ? $theme_data['data']->get( 'Name' ) : '';
	$theme_data_v['vulnerabilities'] = null;
	$theme_data_v['vulnerable']      = 0;

	// Retrieve vulnerabilities for the theme using its slug and version.
	if ( isset( $theme_slug ) && ! empty( $theme_slug ) ) {

		$theme_api_response = wpvulnerability_get_theme( $theme_slug, $theme_version, 0 );

		// If vulnerabilities are found, update the theme data accordingly.
		if ( ! empty( $theme_api_response ) ) {

			$theme_data_v['vulnerabilities'] = $theme_api_response;
			$theme_data_v['vulnerable']      = 1;

		}
	}

	return $theme_data_v;
}

/**
 * Get Installed Themes
 * Retrieves the list of installed themes, checks for vulnerabilities in each of them, caches the data, and sends an email notification if vulnerabilities are detected.
 *
 * @since 2.0.0
 *
 * @return string JSON-encoded array of theme data with vulnerabilities and vulnerable status.
 */
function wpvulnerability_theme_get_installed() {

	$wpvulnerability_themes_vulnerable = 0;

	$themes_v = array();
	$themes   = wp_get_themes();

	foreach ( $themes as $slug => $theme_data ) {

		// Store the theme data.
		$themes_v[ $slug ]['data'] = $theme_data;

		// Get fresh vulnerabilities for the theme.
		$themes_v[ $slug ]['wpvulnerability'] = wpvulnerability_get_fresh_theme_vulnerabilities( $themes_v[ $slug ], $slug );

		// If the theme is vulnerable, increment the vulnerable themes counter.
		if ( isset( $themes_v[ $slug ]['wpvulnerability']['vulnerable'] ) && 1 === (int) $themes_v[ $slug ]['wpvulnerability']['vulnerable'] ) {
			++$wpvulnerability_themes_vulnerable;
		}
	}

	// Update options for multisite installations.
	if ( is_multisite() ) {
				update_site_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
				update_site_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
				update_site_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );

		// Update options for single site installations.
	} else {
				update_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
				update_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
				update_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
	}

	return wp_json_encode( $themes_v );
}

/**
 * Get the cached themes vulnerabilities or update the cache if it's stale or missing.
 *
 * @since 2.0.0
 *
 * @return array Array of installed themes with their vulnerabilities.
 */
function wpvulnerability_theme_get_vulnerabilities() {

	if ( is_multisite() ) {
		// Get the cached theme data and decode it.
		$theme_data_cache = json_decode( get_site_option( 'wpvulnerability-themes-cache' ) );

		// Get the installed theme data and decode it.
		$theme_data = json_decode( get_site_option( 'wpvulnerability-themes' ), true );

	} else {
		// Get the cached theme data and decode it.
		$theme_data_cache = json_decode( get_option( 'wpvulnerability-themes-cache' ) );

		// Get the installed theme data and decode it.
		$theme_data = json_decode( get_option( 'wpvulnerability-themes' ), true );
	}

	// If the cache is stale or the theme data is empty, update the cache.
	if ( ( isset( $theme_data_cache ) && $theme_data_cache < time() ) || empty( $theme_data ) ) {
		// Get the installed theme data and update the cache.
		$theme_data = json_decode( wpvulnerability_theme_get_installed(), true );
	}

	return $theme_data;
}

/**
 * Update the installed themes cache and remove any old cache data.
 *
 * @since 2.0.0
 *
 * @return void
 */
function wpvulnerability_theme_get_vulnerabilities_clean() {
	wpvulnerability_clear_cache( 'themes' );
	wpvulnerability_theme_get_installed();
}

/**
 * Admin Head
 * Adds vulnerability information after the theme row and notices on the theme page based on the installed theme cache.
 *
 * @since 2.0.0
 *
 * @return void
 */
function wpvulnerability_theme_page() {

	// Check if the current page is the themes page.
	global $pagenow;
	if ( wpvulnerability_analyze_filter( 'themes' ) && 'themes.php' === $pagenow && wpvulnerability_capabilities() ) {

		// Get the vulnerabilities for the installed themes.
		$themes = wpvulnerability_theme_get_vulnerabilities();

		// Loop through the themes and add vulnerability information after the theme row for vulnerable themes.
		foreach ( $themes as $theme_file => $theme_data ) {

			if ( isset( $theme_data['wpvulnerability']['vulnerable'] ) && 1 === (int) $theme_data['wpvulnerability']['vulnerable'] ) {
				add_action( 'after_theme_row_' . esc_attr( $theme_file ), 'wpvulnerability_theme_info_after', 10, 2 );
			}
		}
	}
}
// Add notices for vulnerable themes on the theme page.
add_action( 'admin_head', 'wpvulnerability_theme_page' );

/**
 * Filters the themes list to show only vulnerable themes when the "Vulnerable" tab is selected.
 *
 * This function hooks into the WordPress themes listing in the network admin to filter the displayed themes
 * based on their vulnerability status. When the "Vulnerable" tab is selected (identified by the `theme_status=vulnerable`
 * query parameter), it filters the themes list to include only those themes with known vulnerabilities.
 *
 * The function retrieves the vulnerabilities for all themes from the WordPress options table and compares
 * them against the active list of themes. Themes without vulnerabilities are removed from the list, leaving
 * only those that are considered vulnerable.
 *
 * @since 3.3.5
 *
 * @global object $wp_list_table The WordPress list table object for managing themes.
 *
 * @return void
 */
function wpvulnerability_themes_filter() {
	if ( isset( $_GET['theme_status'] ) && 'vulnerable' === $_GET['theme_status'] ) { // phpcs:ignore

		global $wp_list_table;

		// Retrieve the vulnerabilities for all themes from the options table and decode the JSON.
		$theme_vulnerabilities = is_multisite()
			? json_decode( get_site_option( 'wpvulnerability-themes' ), true )
			: json_decode( get_option( 'wpvulnerability-themes' ), true );

		// Loop through the items in the themes list table.
		foreach ( $wp_list_table->items as $theme_file => $theme_data ) {

			if ( ! isset( $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerable'] ) ||
				0 === (int) $theme_vulnerabilities[ $theme_file ]['wpvulnerability']['vulnerable'] ) {

				unset( $wp_list_table->items[ $theme_file ] );
			}
		}
	}
}

/**
 * Initializes the vulnerability filtering for the themes list in the network admin area of a multisite installation.
 *
 * This function checks if the current environment is a multisite network and whether the user is in the network
 * admin area. If both conditions are met, it hooks into the 'admin_head-themes.php' action to apply a filter that
 * shows only vulnerable themes in the themes list.
 *
 * @since 3.3.5
 *
 * @return void
 */
function wpvulnerability_themes_filter_init() {
	if ( is_multisite() && is_network_admin() ) {
		add_action( 'admin_head-themes.php', 'wpvulnerability_themes_filter' );
	}
}
add_action( 'network_admin_menu', 'wpvulnerability_themes_filter_init' );

/**
 * Adds a "Vulnerable" tab to the WordPress themes page that displays the count of vulnerable themes.
 *
 * This function checks the cache for the number of vulnerable themes and adds a new tab to the themes
 * management page in the WordPress admin area. The tab displays the count of vulnerable themes and highlights it
 * if it is currently active. The tab is added only in the network admin area of a multisite installation.
 *
 * @since 3.3.5
 *
 * @param array $views An array of existing theme views (tabs) in the WordPress admin themes page.
 *
 * @return array The modified array of views including the "Vulnerable" tab.
 */
function wpvulnerability_themes_view( $views ) {
	if ( ! wpvulnerability_analyze_filter( 'themes' ) ) {
		return $views;
	}

	$wpvulnerability_themes_count = is_multisite() ? get_site_option( 'wpvulnerability-themes-vulnerable' ) : null;

	$wpvulnerability_themes_total = 0;
	if ( $wpvulnerability_themes_count ) {
		$wpvulnerability_themes_total = json_decode( $wpvulnerability_themes_count );
	}

	if ( is_multisite() && is_network_admin() ) {
		$views['vulnerable'] = sprintf(
			'<a href="%s"%s>%s</a>',
			network_admin_url( 'themes.php?theme_status=vulnerable' ),
			( isset( $_GET['theme_status'] ) && 'vulnerable' === $_GET['theme_status'] ? ' class="current"' : '' ), // phpcs:ignore
			// translators: the number of vulnerabilities.
			sprintf( __( 'Vulnerabilities (%d)', 'wpvulnerability' ), $wpvulnerability_themes_total )
		);
	}

	return $views;
}

/**
 * Adds a custom filter to the themes page in the WordPress admin to display a tab for vulnerable themes.
 *
 * This function hooks into the 'views_themes-network' filter to add a custom tab or view for displaying vulnerable themes
 * on the themes management page in the WordPress network admin area. The tab is added only in a multisite setup
 * and specifically in the network admin context.
 *
 * @since 3.3.5
 *
 * @return void
 */
function wpvulnerability_themes_add_tab() {
	if ( is_multisite() && is_network_admin() ) {
		add_filter( 'views_themes-network', 'wpvulnerability_themes_view' );
	}
}
add_action( 'admin_head', 'wpvulnerability_themes_add_tab' );