| @@ -551,114 +551,46 @@ | ||
| 551 | 551 | return implode( "\n", $rules ); |
| 552 | 552 | } |
| 553 | 553 | |
| 554 | 554 | /** |
| 555 | - * Get security grade based on enabled headers | |
| 555 | + * Warnings about the Content-Security-Policy as it is configured | |
| 556 | 556 | * |
| 557 | - * @return array | |
| 557 | + * They are about what is saved in the tab, not about what reaches the | |
| 558 | + * browser: that is measured by the checks of Security Check, which is | |
| 559 | + * also what the Test Headers button shows. Until 3.0.4 that button graded | |
| 560 | + * these settings and listed them as enabled headers without asking the | |
| 561 | + * site for anything. | |
| 562 | + * | |
| 563 | + * @since 3.0.5 | |
| 564 | + * | |
| 565 | + * @return string[] | |
| 558 | 566 | */ |
| 559 | - public function get_security_grade() { | |
| 560 | - $score = 0; | |
| 561 | - $enabled = array(); | |
| 562 | - $missing = array(); | |
| 567 | + public function get_csp_warnings() { | |
| 563 | 568 | $warnings = array(); |
| 564 | 569 | |
| 565 | - // X-Frame-Options (15 points) | |
| 566 | - if ( ! empty( $this->options['x_frame_options'] ) ) { | |
| 567 | - $score += 15; | |
| 568 | - $enabled[] = 'X-Frame-Options: ' . $this->options['x_frame_options']; | |
| 569 | - } else { | |
| 570 | - $missing[] = 'X-Frame-Options'; | |
| 570 | + if ( empty( $this->options['csp']['enabled'] ) ) { | |
| 571 | + return $warnings; | |
| 571 | 572 | } |
| 572 | 573 | |
| 573 | - // X-Content-Type-Options (15 points) | |
| 574 | - if ( ! empty( $this->options['x_content_type_options'] ) ) { | |
| 575 | - $score += 15; | |
| 576 | - $enabled[] = 'X-Content-Type-Options: nosniff'; | |
| 577 | - } else { | |
| 578 | - $missing[] = 'X-Content-Type-Options'; | |
| 574 | + if ( ! empty( $this->options['csp']['report_only'] ) ) { | |
| 575 | + $warnings[] = __( 'CSP is in report-only mode: the browser reports what the policy would block, and blocks nothing.', 'vigilante' ); | |
| 576 | + return $warnings; | |
| 579 | 577 | } |
| 580 | 578 | |
| 581 | - // HSTS (20 points) | |
| 582 | - if ( ! empty( $this->options['hsts']['enabled'] ) ) { | |
| 583 | - $hsts = $this->options['hsts']; | |
| 584 | - if ( $hsts['max_age'] >= 31536000 ) { | |
| 585 | - $score += 20; | |
| 586 | - } else { | |
| 587 | - $score += 10; | |
| 588 | - $warnings[] = 'HSTS max-age should be at least 1 year (31536000 seconds)'; | |
| 589 | - } | |
| 590 | - $enabled[] = 'Strict-Transport-Security'; | |
| 591 | - } else { | |
| 592 | - $missing[] = 'Strict-Transport-Security (HSTS)'; | |
| 579 | + if ( $this->is_csp_restrictive() ) { | |
| 580 | + $warnings[] = __( 'Restrictive CSP detected. Admin area is automatically excluded to prevent breaking the dashboard and block editor.', 'vigilante' ); | |
| 593 | 581 | } |
| 594 | 582 | |
| 595 | - // CSP (30 points; absorbs the 10 points freed by retiring the deprecated X-XSS-Protection) | |
| 596 | - if ( ! empty( $this->options['csp']['enabled'] ) ) { | |
| 597 | - if ( empty( $this->options['csp']['report_only'] ) ) { | |
| 598 | - $score += 30; | |
| 599 | - $enabled[] = 'Content-Security-Policy'; | |
| 600 | - | |
| 601 | - // Add warning if CSP is restrictive | |
| 602 | - if ( $this->is_csp_restrictive() ) { | |
| 603 | - $warnings[] = __( 'Restrictive CSP detected. Admin area is automatically excluded to prevent breaking the dashboard and block editor.', 'vigilante' ); | |
| 604 | - } | |
| 605 | - | |
| 606 | - // Check for specific compatibility issues | |
| 607 | - $csp_issues = $this->get_csp_compatibility_issues(); | |
| 608 | - foreach ( $csp_issues as $issue ) { | |
| 609 | - if ( 'high' === $issue['severity'] ) { | |
| 610 | - $warnings[] = sprintf( | |
| 611 | - /* translators: 1: CSP directive name, 2: Issue description */ | |
| 612 | - __( 'CSP %1$s: %2$s', 'vigilante' ), | |
| 613 | - $issue['directive'], | |
| 614 | - $issue['issue'] | |
| 615 | - ); | |
| 616 | - } | |
| 617 | - } | |
| 618 | - } else { | |
| 619 | - $score += 15; | |
| 620 | - $enabled[] = 'Content-Security-Policy-Report-Only'; | |
| 621 | - $warnings[] = 'CSP is in report-only mode (recommended for testing)'; | |
| 583 | + foreach ( $this->get_csp_compatibility_issues() as $issue ) { | |
| 584 | + if ( 'high' === $issue['severity'] ) { | |
| 585 | + $warnings[] = sprintf( | |
| 586 | + /* translators: 1: CSP directive name, 2: Issue description */ | |
| 587 | + __( 'CSP %1$s: %2$s', 'vigilante' ), | |
| 588 | + $issue['directive'], | |
| 589 | + $issue['issue'] | |
| 590 | + ); | |
| 622 | 591 | } |
| 623 | - } else { | |
| 624 | - $missing[] = 'Content-Security-Policy'; | |
| 625 | 592 | } |
| 626 | 593 | |
| 627 | - // Referrer-Policy (10 points) | |
| 628 | - if ( ! empty( $this->options['referrer_policy'] ) ) { | |
| 629 | - $score += 10; | |
| 630 | - $enabled[] = 'Referrer-Policy: ' . $this->options['referrer_policy']; | |
| 631 | - } else { | |
| 632 | - $missing[] = 'Referrer-Policy'; | |
| 633 | - } | |
| 634 | - | |
| 635 | - // Permissions-Policy (10 points) | |
| 636 | - if ( ! empty( $this->options['permissions_policy']['enabled'] ) ) { | |
| 637 | - $score += 10; | |
| 638 | - $enabled[] = 'Permissions-Policy'; | |
| 639 | - } else { | |
| 640 | - $missing[] = 'Permissions-Policy'; | |
| 641 | - } | |
| 642 | - | |
| 643 | - // Calculate grade | |
| 644 | - if ( $score >= 90 ) { | |
| 645 | - $grade = 'A'; | |
| 646 | - } elseif ( $score >= 80 ) { | |
| 647 | - $grade = 'B'; | |
| 648 | - } elseif ( $score >= 70 ) { | |
| 649 | - $grade = 'C'; | |
| 650 | - } elseif ( $score >= 60 ) { | |
| 651 | - $grade = 'D'; | |
| 652 | - } else { | |
| 653 | - $grade = 'F'; | |
| 654 | - } | |
| 655 | - | |
| 656 | - return array( | |
| 657 | - 'grade' => $grade, | |
| 658 | - 'score' => $score, | |
| 659 | - 'headers' => $enabled, | |
| 660 | - 'missing' => $missing, | |
| 661 | - 'warnings' => $warnings, | |
| 662 | - ); | |
| 594 | + return $warnings; | |
| 663 | 595 | } |
| 664 | 596 | } |