66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
"""Data models for the CIS NGINX benchmark auditor."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
|
|
# Possible check outcomes
|
|
PASS = "PASS"
|
|
FAIL = "FAIL"
|
|
MANUAL = "MANUAL" # check ran but result needs human judgment
|
|
ERROR = "ERROR" # could not run the check (command failed, not found, etc.)
|
|
|
|
|
|
@dataclass
|
|
class CheckResult:
|
|
"""Result of running a single CIS check."""
|
|
check_id: str
|
|
title: str
|
|
profiles: List[str] # which CIS profiles this check belongs to
|
|
status: str # PASS | FAIL | MANUAL | ERROR
|
|
actual_value: str # what was found on the system
|
|
expected_value: str # what the benchmark requires
|
|
commands_run: List[str] # shell commands executed
|
|
remediation: str # how to fix (from benchmark)
|
|
notes: str = "" # extra context or error detail
|
|
|
|
|
|
@dataclass
|
|
class AuditReport:
|
|
"""Complete audit result for a target system."""
|
|
target: str # hostname or "localhost"
|
|
profile: str # e.g. "Level 1 - Webserver"
|
|
timestamp: datetime = field(default_factory=datetime.utcnow)
|
|
results: List[CheckResult] = field(default_factory=list)
|
|
|
|
# ── convenience aggregates ──────────────────────────────────────────────
|
|
|
|
@property
|
|
def passed(self) -> List[CheckResult]:
|
|
return [r for r in self.results if r.status == PASS]
|
|
|
|
@property
|
|
def failed(self) -> List[CheckResult]:
|
|
return [r for r in self.results if r.status == FAIL]
|
|
|
|
@property
|
|
def manual(self) -> List[CheckResult]:
|
|
return [r for r in self.results if r.status == MANUAL]
|
|
|
|
@property
|
|
def errors(self) -> List[CheckResult]:
|
|
return [r for r in self.results if r.status == ERROR]
|
|
|
|
@property
|
|
def total(self) -> int:
|
|
return len(self.results)
|
|
|
|
def summary(self) -> str:
|
|
return (
|
|
f"Target: {self.target} | Profile: {self.profile} | "
|
|
f"Total: {self.total} | PASS: {len(self.passed)} | "
|
|
f"FAIL: {len(self.failed)} | MANUAL: {len(self.manual)} | "
|
|
f"ERROR: {len(self.errors)}"
|
|
)
|