298 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace WPScan;
 | |
| 
 | |
| // Exit if accessed directly.
 | |
| defined( 'ABSPATH' ) || exit;
 | |
| 
 | |
| /**
 | |
|  * Report.
 | |
|  *
 | |
|  * Generates the main report.
 | |
|  *
 | |
|  * @since 1.0.0
 | |
|  */
 | |
| class Report
 | |
| {
 | |
| 	// Page slug.
 | |
| 	public $page;
 | |
| 
 | |
| 	/**
 | |
| 	 * Class constructor.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param object $parent parent.
 | |
| 	 * @access public
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public function __construct( $parent ) {
 | |
| 		$this->parent = $parent;
 | |
| 		$this->page   = 'wpscan';
 | |
| 
 | |
| 		add_action( 'admin_menu', array( $this, 'menu' ) );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Admin menu
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public function menu() {
 | |
| 		$title = __( 'Report', 'wpscan' );
 | |
| 
 | |
| 		add_submenu_page(
 | |
| 			'wpscan',
 | |
| 			$title,
 | |
| 			$title,
 | |
| 			$this->parent->WPSCAN_ROLE,
 | |
| 			$this->page,
 | |
| 			array( $this, 'page' )
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Render report page
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function page() {
 | |
| 		include $this->parent->plugin_dir . '/views/report.php';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * List vulnerabilities on screen
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 *
 | |
| 	 * @param string $type - Type of report: WordPress, plugins, themes.
 | |
| 	 * @param string $name - key name of the element.
 | |
| 	 *
 | |
| 	 * @access public
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function list_api_vulnerabilities( $type, $name ) {
 | |
| 		$report  = $this->parent->get_report();
 | |
| 		$ignored = $this->parent->get_ignored_vulnerabilities();
 | |
| 
 | |
| 		$null_text        = __( 'No known vulnerabilities found to affect this version', 'wpscan' );
 | |
| 		$not_checked_text = __( 'Not checked yet. Click the Run All button to run a scan', 'wpscan' );
 | |
| 
 | |
| 		if ( empty( $report ) || ! isset( $report[ $type ] ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		$report = $report[ $type ];
 | |
| 
 | |
| 		if ( array_key_exists( $name, $report ) ) {
 | |
| 			$report = $report[ $name ];
 | |
| 		} else {
 | |
| 			echo esc_html( $not_checked_text );
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $report['vulnerabilities'] ) ) {
 | |
| 			$list = array();
 | |
| 
 | |
| 			usort( $report['vulnerabilities'], array( 'self', 'sort_vulnerabilities' ) );
 | |
| 
 | |
| 			foreach ( $report['vulnerabilities'] as $item ) {
 | |
| 				$id = 'security-checks' === $type ? $item['id'] : $item->id;
 | |
| 
 | |
| 				if ( in_array( $id, $ignored, true ) ) {
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				$html  = '<div class="vulnerability">';
 | |
| 				$html .= $this->vulnerability_severity( $item );
 | |
| 				$html .= '<a href="' . esc_url( 'https://wpscan.com/vulnerability/' . $item->id ) . '" target="_blank">';
 | |
| 				$html .= $this->parent->get_sanitized_vulnerability_title( $item );
 | |
| 				$html .= '</a>';
 | |
| 				$html .= '</div>';
 | |
| 
 | |
| 				$list[] = $html;
 | |
| 			}
 | |
| 
 | |
| 			echo empty( $list ) ? $null_text : join( '<br>', $list );
 | |
| 
 | |
| 		} else {
 | |
| 			echo esc_html( $null_text );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sort vulnerabilities by severity
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return int
 | |
| 	 */
 | |
| 	public function sort_vulnerabilities( $a, $b ) {
 | |
| 		$a = isset( $a->cvss->score ) ? intval( $a->cvss->score ) : 0;
 | |
| 		$b = isset( $b->cvss->score ) ? intval( $b->cvss->score ) : 0;
 | |
| 
 | |
| 		return $b > $a ? 1 : -1;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Vulnerability severity
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function vulnerability_severity( $vulnerability ) {
 | |
| 		$plan = $this->parent->classes['account']->get_account_status()['plan'];
 | |
| 
 | |
| 		if ( 'enterprise' !== $plan ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$html = "<div class='vulnerability-severity'>";
 | |
| 
 | |
| 		if ( isset( $vulnerability->cvss->severity ) ) {
 | |
| 			$severity = $vulnerability->cvss->severity;
 | |
| 			$html    .= "<span class='wpscan-" . esc_attr( $severity ) . "'>" . esc_html( $severity ) . '</span>';
 | |
| 		}
 | |
| 
 | |
| 		$html .= '</div>';
 | |
| 
 | |
| 		return $html;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Is the plugin/theme is closed
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function is_item_closed( $type, $name ) {
 | |
| 		$report = $this->parent->get_report();
 | |
| 
 | |
| 		if ( empty( $report ) || ! isset( $report[ $type ] ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		$report = $report[ $type ];
 | |
| 
 | |
| 		if ( array_key_exists( $name, $report ) ) {
 | |
| 			$report = $report[ $name ];
 | |
| 		}
 | |
| 
 | |
| 		return isset( $report['closed'] ) ? $report['closed'] : false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get all vulnerabilities
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @access public
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function get_all_vulnerabilities() {
 | |
| 		$ret     = array();
 | |
| 		$report  = $this->parent->get_report();
 | |
| 		$ignored = $this->parent->get_ignored_vulnerabilities();
 | |
| 
 | |
| 		$types = array( 'wordpress', 'plugins', 'themes', 'security-checks' );
 | |
| 
 | |
| 		foreach ( $types as $type ) {
 | |
| 			if ( isset( $report[ $type ] ) ) {
 | |
| 
 | |
| 				foreach ( $report[ $type ] as $item ) {
 | |
| 					if ( isset( $item['vulnerabilities'] ) ) {
 | |
| 						foreach ( $item['vulnerabilities'] as $vuln ) {
 | |
| 							$id    = 'security-checks' === $type ? $vuln['id'] : $vuln->id;
 | |
| 							$title = 'security-checks' === $type ? $vuln['title'] : $this->parent->get_sanitized_vulnerability_title( $vuln );
 | |
| 
 | |
| 							if ( in_array( $id, $ignored, true ) ) {
 | |
| 								continue;
 | |
| 							}
 | |
| 
 | |
| 							$ret[] = $title;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get item non-ignored vulnerabilities count
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 *
 | |
| 	 * @param string $type - Type of report: WordPress, plugins, themes.
 | |
| 	 * @param string $name - key name of the element.
 | |
| 	 *
 | |
| 	 * @access public
 | |
| 	 * @return int|null
 | |
| 	 */
 | |
| 	public function get_item_vulnerabilities_count( $type, $name ) {
 | |
| 		$report  = $this->parent->get_report();
 | |
| 		$ignored = $this->parent->get_ignored_vulnerabilities();
 | |
| 
 | |
| 		if ( empty( $report ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $report[ $type ] ) && array_key_exists( $name, $report[ $type ] ) ) {
 | |
| 			$report = $report[ $type ][ $name ];
 | |
| 		}
 | |
| 
 | |
| 		foreach ( $report['vulnerabilities'] as $key => &$item ) {
 | |
| 			$id = 'security-checks' === $type ? $item['id'] : $item->id;
 | |
| 
 | |
| 			if ( in_array( $id, $ignored, true ) ) {
 | |
| 				unset( $report['vulnerabilities'][ $key ] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return count( $report['vulnerabilities'] );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Show status icons: checked, attention and error
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 *
 | |
| 	 * @param string $type - Type of report: WordPress, plugins, themes.
 | |
| 	 * @param string $name - key name of the element.
 | |
| 	 *
 | |
| 	 * @access public
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function get_status( $type, $name ) {
 | |
| 		$report  = $this->parent->get_report();
 | |
| 		$ignored = $this->parent->get_ignored_vulnerabilities();
 | |
| 
 | |
| 		if ( empty( $report ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $report[ $type ] ) && array_key_exists( $name, $report[ $type ] ) ) {
 | |
| 			$report = $report[ $type ][ $name ];
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if ( array_key_exists( 'not_found', $report ) ) {
 | |
| 			$icon = 'dashicons-yes is-green';
 | |
| 		} elseif ( ! isset( $report['vulnerabilities'] ) ) {
 | |
| 			$icon = 'dashicons-no-alt is-gray';
 | |
| 		} elseif ( empty( $report['vulnerabilities'] ) ) {
 | |
| 			$icon = 'dashicons-yes is-green';
 | |
| 		} else {
 | |
| 			$count = $this->get_item_vulnerabilities_count( $type, $name );
 | |
| 			$icon  = 0 === $count ? 'dashicons-yes is-green' : 'dashicons-warning is-red';
 | |
| 		}
 | |
| 
 | |
| 		return "  <span class='dashicons $icon'></span>";
 | |
| 	}
 | |
| }
 |