| @@ -16,8 +16,9 @@ | ||
| 16 | 16 | use WP_Defender\Traits\Plugin; |
| 17 | 17 | use WP_Defender\Model\Scan_Item; |
| 18 | 18 | use WP_Defender\Behavior\WPMUDEV; |
| 19 | 19 | use WP_Defender\Helper\Analytics\Scan as Scan_Analytics; |
| 20 | +use WP_Defender\Component\Scan as Scan_Component; | |
| 20 | 21 | |
| 21 | 22 | /** |
| 22 | 23 | * Represents a component responsible for checking known vulnerabilities. |
| 23 | 24 | */ |
| @@ -90,29 +91,41 @@ | ||
| 90 | 91 | * @return array |
| 91 | 92 | */ |
| 92 | 93 | private function gather_fact( string $type, &$info_cached ): array { |
| 93 | 94 | $items = array(); |
| 94 | - $model = Scan::get_last(); | |
| 95 | 95 | |
| 96 | 96 | if ( 'plugin' === $type ) { |
| 97 | - $get_items = $this->get_plugins(); | |
| 97 | + $actioned_plugins = get_site_option( Scan_Component::PLUGINS_ACTIONED ); | |
| 98 | + if ( Scan_Component::are_actioned_plugins( $actioned_plugins ) ) { | |
| 99 | + foreach ( $actioned_plugins as $slug => $plugin_data ) { | |
| 100 | + $items[ $slug ] = $plugin_data['Version']; | |
| 101 | + $info_cached[ $slug ] = array( $plugin_data['Name'], $plugin_data['Version'], $plugin_data['Slug'] ); | |
| 102 | + } | |
| 103 | + } else { | |
| 104 | + // An emergency option if for some reason the plugin's quick data is not saved in the first step. | |
| 105 | + foreach ( $this->get_plugins() as $slug => $plugin ) { | |
| 106 | + $base_slug = $this->get_plugin_slug_by( $slug ); | |
| 107 | + $items[ $base_slug ] = $plugin['Version']; | |
| 108 | + $info_cached[ $base_slug ] = array( $plugin['Name'], $plugin['Version'], $slug ); | |
| 109 | + } | |
| 110 | + } | |
| 98 | 111 | } elseif ( 'theme' === $type ) { |
| 99 | - $get_items = $this->get_themes(); | |
| 112 | + $model = Scan::get_last(); | |
| 113 | + foreach ( $this->get_themes() as $slug => $item ) { | |
| 114 | + // It's actual because we don't have ignored theme slugs. | |
| 115 | + if ( is_object( $model ) && $model->is_issue_ignored( $slug ) ) { | |
| 116 | + continue; | |
| 117 | + } | |
| 118 | + | |
| 119 | + $base_slug = explode( '/', $slug ); | |
| 120 | + $base_slug = array_shift( $base_slug ); | |
| 121 | + $items[ $base_slug ] = $item['Version']; | |
| 122 | + $info_cached[ $base_slug ] = array( $item['Name'], $item['Version'], $slug ); | |
| 123 | + } | |
| 100 | 124 | } else { |
| 101 | 125 | return $items; |
| 102 | 126 | } |
| 103 | 127 | |
| 104 | - foreach ( $get_items as $slug => $item ) { | |
| 105 | - if ( is_object( $model ) && $model->is_issue_ignored( $slug ) ) { | |
| 106 | - continue; | |
| 107 | - } | |
| 108 | - | |
| 109 | - $base_slug = explode( '/', $slug ); | |
| 110 | - $base_slug = array_shift( $base_slug ); | |
| 111 | - $items[ $base_slug ] = $item['Version']; | |
| 112 | - $info_cached[ $base_slug ] = array( $item['Name'], $item['Version'], $slug ); | |
| 113 | - } | |
| 114 | - | |
| 115 | 128 | return $items; |
| 116 | 129 | } |
| 117 | 130 | |
| 118 | 131 | /** |
| @@ -183,9 +196,9 @@ | ||
| 183 | 196 | * @param string $type The type of the vulnerability check. |
| 184 | 197 | * @param array $ignored_issues The ignored issues. |
| 185 | 198 | */ |
| 186 | 199 | private function process_result( $result, $info, $type, $ignored_issues ) { |
| 187 | - if ( empty( $result ) ) { | |
| 200 | + if ( ! is_array( $result ) || array() === $result ) { | |
| 188 | 201 | return; |
| 189 | 202 | } |
| 190 | 203 | |
| 191 | 204 | $this->log( sprintf( 'Checking %s:', $type ) ); |
| @@ -194,9 +207,9 @@ | ||
| 194 | 207 | foreach ( $result as $base_slug => $bugs ) { |
| 195 | 208 | if ( 'wp_core' === $type ) { |
| 196 | 209 | global $wp_version; |
| 197 | 210 | $is_exist = false; |
| 198 | - if ( ! empty( $ignored_issues ) ) { | |
| 211 | + if ( is_array( $ignored_issues ) && array() !== $ignored_issues ) { | |
| 199 | 212 | foreach ( $ignored_issues as $issue ) { |
| 200 | 213 | if ( $wp_version === $issue->raw_data['version'] ) { |
| 201 | 214 | $is_exist = true; |
| 202 | 215 | } |
| @@ -221,30 +234,45 @@ | ||
| 221 | 234 | ), |
| 222 | 235 | 'new_structure' => '3.4.0', |
| 223 | 236 | ); |
| 224 | 237 | |
| 225 | - $model->add_item( Scan_Item::TYPE_VULNERABILITY, $raw_data ); | |
| 238 | + if ( array() !== $raw_data['bugs'] ) { | |
| 239 | + $model->add_item( Scan_Item::TYPE_VULNERABILITY, $raw_data ); | |
| 240 | + } | |
| 226 | 241 | } |
| 227 | 242 | } elseif ( 'plugin' === $type || 'theme' === $type ) { |
| 228 | - if ( empty( $info[ $base_slug ] ) ) { | |
| 243 | + if ( ! isset( $info[ $base_slug ] ) || ! is_array( $info[ $base_slug ] ) ) { | |
| 229 | 244 | continue; |
| 230 | 245 | } |
| 231 | 246 | |
| 232 | - [ $name, $current_version, $slug ] = $info[ $base_slug ]; | |
| 233 | - $raw_data = array( | |
| 234 | - 'type' => $type, | |
| 235 | - 'slug' => $slug, | |
| 236 | - 'base_slug' => $base_slug, | |
| 237 | - 'version' => $current_version, | |
| 238 | - 'name' => $name, | |
| 239 | - 'bugs' => array(), | |
| 240 | - ); | |
| 241 | - | |
| 242 | 247 | if ( isset( $bugs['confirmed'] ) && ( is_array( $bugs['confirmed'] ) || $bugs['confirmed'] instanceof Countable ? count( $bugs['confirmed'] ) : 0 ) ) { |
| 243 | - if ( 'plugin' === $type && ! $this->is_likely_wporg_slug( $base_slug ) ) { | |
| 244 | - continue; | |
| 248 | + // Default conditions for wp.org and Pro plugins. | |
| 249 | + $wp_org_plugin_details = array( | |
| 250 | + 'is_existed' => false, | |
| 251 | + 'last_version' => '0', | |
| 252 | + ); | |
| 253 | + if ( 'plugin' === $type && $this->is_likely_wporg_slug( $base_slug ) ) { | |
| 254 | + // Additional conditions for wp.org plugin. | |
| 255 | + $results = $this->handle_wp_org_response_by( $base_slug ); | |
| 256 | + if ( $results['success'] && isset( $results['body']['version'] ) ) { | |
| 257 | + $wp_org_plugin_details = array( | |
| 258 | + 'is_existed' => true, | |
| 259 | + 'last_version' => $results['body']['version'], | |
| 260 | + ); | |
| 261 | + } | |
| 262 | + // If there is not a plugin version, likely the plugin is closed. We'd not display Update button. | |
| 245 | 263 | } |
| 246 | 264 | |
| 265 | + [ $name, $current_version, $slug ] = $info[ $base_slug ]; | |
| 266 | + $raw_data = array( | |
| 267 | + 'type' => $type, | |
| 268 | + 'slug' => $slug, | |
| 269 | + 'base_slug' => $base_slug, | |
| 270 | + 'version' => $current_version, | |
| 271 | + 'name' => $name, | |
| 272 | + 'bugs' => array(), | |
| 273 | + ); | |
| 274 | + | |
| 247 | 275 | $confirmed_bugs = (array) $bugs['confirmed']; |
| 248 | 276 | // @since 3.4.0 Restructure the view in relation to the new CVSS Score field. |
| 249 | 277 | $raw_data['new_structure'] = '3.4.0'; |
| 250 | 278 | $this->log( sprintf( '%s has %d known bugs', $slug, count( $confirmed_bugs ) ) ); |
| @@ -249,8 +277,14 @@ | ||
| 249 | 277 | $raw_data['new_structure'] = '3.4.0'; |
| 250 | 278 | $this->log( sprintf( '%s has %d known bugs', $slug, count( $confirmed_bugs ) ) ); |
| 251 | 279 | |
| 252 | 280 | foreach ( $confirmed_bugs as $bug ) { |
| 281 | + if ( 'plugin' === $type && $wp_org_plugin_details['is_existed'] | |
| 282 | + && version_compare( $wp_org_plugin_details['last_version'], $bug['fixed_in'], '<=' ) | |
| 283 | + ) { | |
| 284 | + // This is another plugin but with the same slug. | |
| 285 | + continue; | |
| 286 | + } | |
| 253 | 287 | $raw_data['bugs'][] = array( |
| 254 | 288 | 'vuln_type' => $bug['vuln_type'], |
| 255 | 289 | 'title' => $bug['title'], |
| 256 | 290 | 'ref' => $bug['references'], |
| @@ -258,9 +292,11 @@ | ||
| 258 | 292 | 'cvss_score' => $bug['cvss']['score'] ?? '', |
| 259 | 293 | ); |
| 260 | 294 | } |
| 261 | 295 | |
| 262 | - $model->add_item( Scan_Item::TYPE_VULNERABILITY, $raw_data ); | |
| 296 | + if ( array() !== $raw_data['bugs'] ) { | |
| 297 | + $model->add_item( Scan_Item::TYPE_VULNERABILITY, $raw_data ); | |
| 298 | + } | |
| 263 | 299 | } |
| 264 | 300 | } |
| 265 | 301 | } |
| 266 | 302 | } |
| @@ -270,11 +306,26 @@ | ||
| 270 | 306 | * |
| 271 | 307 | * @param array $ignored_issues The array of ignored vulnerability issues to add. |
| 272 | 308 | */ |
| 273 | 309 | private function add_ignored_issues( $ignored_issues ) { |
| 274 | - if ( ! empty( $ignored_issues ) ) { | |
| 275 | - foreach ( $ignored_issues as $issue ) { | |
| 276 | - $this->scan->add_item( Scan_Item::TYPE_VULNERABILITY, $issue->raw_data, Scan_Item::STATUS_IGNORE ); | |
| 310 | + if ( ! is_array( $ignored_issues ) || array() === $ignored_issues ) { | |
| 311 | + return; | |
| 312 | + } | |
| 313 | + | |
| 314 | + // wp_core issues have no slug, so carry them forward as-is. | |
| 315 | + $wp_core_issues = array(); | |
| 316 | + $other_issues = array(); | |
| 317 | + foreach ( $ignored_issues as $issue ) { | |
| 318 | + if ( 'wp_core' === ( $issue->raw_data['type'] ?? '' ) ) { | |
| 319 | + $wp_core_issues[] = $issue; | |
| 320 | + } else { | |
| 321 | + $other_issues[] = $issue; | |
| 277 | 322 | } |
| 278 | 323 | } |
| 324 | + | |
| 325 | + foreach ( $wp_core_issues as $issue ) { | |
| 326 | + $this->scan->add_item( Scan_Item::TYPE_VULNERABILITY, $issue->raw_data, Scan_Item::STATUS_IGNORE ); | |
| 327 | + } | |
| 328 | + | |
| 329 | + $this->scan->carry_forward_ignored_issues( Scan_Item::TYPE_VULNERABILITY, $other_issues, 'slug' ); | |
| 279 | 330 | } |
| 280 | 331 | } |