69 lines
2.3 KiB
PHP
69 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Server;
|
|
|
|
/**
|
|
* Turns a server's hardening state into a single security-posture SCORE. Reuses
|
|
* HardeningService::state() (the CIS-relevant gates: SSH root/password, fail2ban, firewall) rather
|
|
* than re-probing. `neutral` items (auto-updates = an operator preference, not a gate) and
|
|
* OS-unsupported items don't count toward the score. The remediation for a failing check is the
|
|
* existing hardening toggle on the server-details page.
|
|
*/
|
|
class PostureService
|
|
{
|
|
public function __construct(private HardeningService $hardening) {}
|
|
|
|
/**
|
|
* @return array{score:int, rating:string, passed:int, applicable:int,
|
|
* checks:array<int, array{key:string,label:string,detail:string,secure:bool,supported:bool,neutral:bool,counts:bool}>}
|
|
*/
|
|
public function score(Server $server): array
|
|
{
|
|
$items = $this->hardening->state($server); // throws if the host can't be read
|
|
|
|
$checks = [];
|
|
$passed = 0;
|
|
$applicable = 0;
|
|
|
|
foreach ($items as $it) {
|
|
// A check counts toward the score only when the OS supports it AND it's a real security
|
|
// gate (not the neutral auto-updates preference).
|
|
$counts = $it['supported'] && ! $it['neutral'];
|
|
if ($counts) {
|
|
$applicable++;
|
|
if ($it['secure']) {
|
|
$passed++;
|
|
}
|
|
}
|
|
|
|
$checks[] = [
|
|
'key' => $it['key'],
|
|
'label' => $it['label'],
|
|
'detail' => $it['detail'],
|
|
'secure' => (bool) $it['secure'],
|
|
'supported' => (bool) $it['supported'],
|
|
'neutral' => (bool) $it['neutral'],
|
|
'counts' => $counts,
|
|
];
|
|
}
|
|
|
|
$score = $applicable > 0 ? (int) round($passed / $applicable * 100) : 100;
|
|
|
|
return [
|
|
'score' => $score,
|
|
'rating' => $this->rating($score),
|
|
'passed' => $passed,
|
|
'applicable' => $applicable,
|
|
'checks' => $checks,
|
|
];
|
|
}
|
|
|
|
/** strong ≥90 · fair ≥60 · weak below — mapped to the status triad in the UI. */
|
|
private function rating(int $score): string
|
|
{
|
|
return $score >= 90 ? 'strong' : ($score >= 60 ? 'fair' : 'weak');
|
|
}
|
|
}
|