CIS_Benchmarks/nginx_audit/models.py
2026-06-18 20:47:15 +03:00

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)}"
)