| @@ -31,8 +31,15 @@ | ||
| 31 | 31 | // is total_max_points(), which sums the declared "max" of every category |
| 32 | 32 | // in get_categories(). Bump this only when manually verifying the sum. |
| 33 | 33 | const TOTAL_MAX_POINTS = 106; |
| 34 | 34 | const REGRESSION_THRESHOLD = 10; // Points dropped before sending the alert email. |
| 35 | + /* | |
| 36 | + * Highest score a site can show while the self-protection check reports | |
| 37 | + * tampering: every other result of this report was computed by the same | |
| 38 | + * code whose files were reported as changed, so a high number next to it | |
| 39 | + * would be a lie. 29 is the top of grade E in compute_grade(). | |
| 40 | + */ | |
| 41 | + const SCORE_CAP_ON_TAMPER = 29; | |
| 35 | 42 | |
| 36 | 43 | /** |
| 37 | 44 | * Sum of the declared "max" of every category. This is the canonical |
| 38 | 45 | * maximum a scan can earn. Using this instead of the constant prevents |
| @@ -96,9 +103,11 @@ | ||
| 96 | 103 | 'label' => __( 'Internal Checks (exclusive)', 'vigilante' ), |
| 97 | 104 | // Sum of the max values of every check in |
| 98 | 105 | // Vigilante_SA_Category_Internal. Update when adding/removing |
| 99 | 106 | // checks or changing their max value. |
| 100 | - 'max' => 30, | |
| 107 | + // 3.0.0: 30 -> 40 (self_integrity check added, worth 10: the | |
| 108 | + // integrity of the plugin that runs every other check). | |
| 109 | + 'max' => 40, | |
| 101 | 110 | ), |
| 102 | 111 | 'reputation' => array( |
| 103 | 112 | 'slug' => 'reputation', |
| 104 | 113 | 'label' => __( 'Reputation / Blacklists', 'vigilante' ), |
| @@ -190,10 +199,16 @@ | ||
| 190 | 199 | $this->persist_scan( $report, $phase ); |
| 191 | 200 | |
| 192 | 201 | // Push to history only when the whole scan has finished (either 'all' in one call, |
| 193 | 202 | // or a 'slow' phase that immediately follows a 'fast' phase within the same minute). |
| 194 | - if ( 'all' === $phase || 'slow' === $phase ) { | |
| 203 | + if ( 'all' === $phase ) { | |
| 195 | 204 | $this->push_history( $report ); |
| 205 | + } elseif ( 'slow' === $phase ) { | |
| 206 | + // The slow phase closes a scan the fast one started, so what goes | |
| 207 | + // to the history is the two merged, as persist_scan() has just | |
| 208 | + // stored them. The report of this phase alone carries only its own | |
| 209 | + // checks, and the trend showed every manual scan as a collapse. | |
| 210 | + $this->push_history( $this->get_last_scan() ); | |
| 196 | 211 | } |
| 197 | 212 | |
| 198 | 213 | return $report; |
| 199 | 214 | } |
| @@ -261,8 +276,24 @@ | ||
| 261 | 276 | // Normalize against the declared category max so skipped checks don't erode the score. |
| 262 | 277 | $declared_max = self::total_max_points(); |
| 263 | 278 | $grade = Vigilante_SA_Helpers::compute_grade( $total_earned, $declared_max ); |
| 264 | 279 | |
| 280 | + // Self-protection caps the score (see SCORE_CAP_ON_TAMPER). The same | |
| 281 | + // cap is applied when phases are merged, in rebuild_from_categories(). | |
| 282 | + $capped_by = ''; | |
| 283 | + foreach ( $results as $r ) { | |
| 284 | + if ( $r instanceof Vigilante_SA_Check_Result | |
| 285 | + && 'self_integrity' === $r->id | |
| 286 | + && Vigilante_SA_Check_Result::STATE_FAIL === $r->state ) { | |
| 287 | + $capped_by = 'self_integrity'; | |
| 288 | + break; | |
| 289 | + } | |
| 290 | + } | |
| 291 | + if ( 'self_integrity' === $capped_by && $grade['score'] > self::SCORE_CAP_ON_TAMPER ) { | |
| 292 | + $grade['score'] = self::SCORE_CAP_ON_TAMPER; | |
| 293 | + $grade['grade'] = 'E'; | |
| 294 | + } | |
| 295 | + | |
| 265 | 296 | return array( |
| 266 | 297 | 'ran_at' => $started, |
| 267 | 298 | 'phase' => $phase, |
| 268 | 299 | 'total_earned' => $total_earned, |
| @@ -269,8 +300,9 @@ | ||
| 269 | 300 | 'total_max' => $declared_max, |
| 270 | 301 | 'total_evaluated'=> $total_max, // Actual evaluated max (excluding skipped). |
| 271 | 302 | 'score' => $grade['score'], |
| 272 | 303 | 'grade' => $grade['grade'], |
| 304 | + 'capped_by' => $capped_by, | |
| 273 | 305 | 'counts' => $counts, |
| 274 | 306 | 'categories' => $categories, |
| 275 | 307 | ); |
| 276 | 308 | } |
| @@ -392,8 +424,24 @@ | ||
| 392 | 424 | |
| 393 | 425 | $total_max = self::total_max_points(); |
| 394 | 426 | $grade = Vigilante_SA_Helpers::compute_grade( $total_earned, $total_max ); |
| 395 | 427 | |
| 428 | + // Self-protection caps the score (see SCORE_CAP_ON_TAMPER). | |
| 429 | + $capped_by = ''; | |
| 430 | + foreach ( $categories as $cat ) { | |
| 431 | + foreach ( (array) $cat['checks'] as $c ) { | |
| 432 | + if ( 'self_integrity' === ( isset( $c['id'] ) ? $c['id'] : '' ) | |
| 433 | + && Vigilante_SA_Check_Result::STATE_FAIL === ( isset( $c['state'] ) ? $c['state'] : '' ) ) { | |
| 434 | + $capped_by = 'self_integrity'; | |
| 435 | + break 2; | |
| 436 | + } | |
| 437 | + } | |
| 438 | + } | |
| 439 | + if ( 'self_integrity' === $capped_by && $grade['score'] > self::SCORE_CAP_ON_TAMPER ) { | |
| 440 | + $grade['score'] = self::SCORE_CAP_ON_TAMPER; | |
| 441 | + $grade['grade'] = 'E'; | |
| 442 | + } | |
| 443 | + | |
| 396 | 444 | return array( |
| 397 | 445 | 'categories' => $categories, |
| 398 | 446 | 'total_earned' => $total_earned, |
| 399 | 447 | 'total_max' => $total_max, |
| @@ -399,8 +447,9 @@ | ||
| 399 | 447 | 'total_max' => $total_max, |
| 400 | 448 | 'total_evaluated'=> $total_evaluated, |
| 401 | 449 | 'score' => $grade['score'], |
| 402 | 450 | 'grade' => $grade['grade'], |
| 451 | + 'capped_by' => $capped_by, | |
| 403 | 452 | 'counts' => $counts, |
| 404 | 453 | ); |
| 405 | 454 | } |
| 406 | 455 | |