scheduling and jobs, new dataclasses and such better UDP handling
This commit is contained in:
@@ -6,8 +6,10 @@ import os
|
||||
import yaml
|
||||
import logging
|
||||
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -25,6 +27,7 @@ class ScanOptions:
|
||||
"""
|
||||
Feature toggles that affect how scans are executed.
|
||||
"""
|
||||
cron: Optional[str] = None
|
||||
udp_scan: bool = False
|
||||
tls_security_scan: bool = True
|
||||
tls_exp_check: bool = True
|
||||
@@ -38,6 +41,8 @@ class Reporting:
|
||||
report_name: str = "Scan Report"
|
||||
report_filename: str = "report.html"
|
||||
full_details: bool = False
|
||||
email_to: List[str] = field(default_factory=list)
|
||||
email_cc: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -58,7 +63,7 @@ class ScanConfigRepository:
|
||||
Search order for the config directory:
|
||||
1) Explicit path argument to load_all()
|
||||
2) Environment variable SCAN_TARGETS_DIR
|
||||
3) Default: /data/scan_targets
|
||||
3) Default: /app/data/scan_targets
|
||||
"""
|
||||
|
||||
SUPPORTED_EXT = (".yaml", ".yml")
|
||||
@@ -102,7 +107,7 @@ class ScanConfigRepository:
|
||||
env = os.getenv("SCAN_TARGETS_DIR")
|
||||
if env:
|
||||
return Path(env)
|
||||
return Path("/data/scan_targets")
|
||||
return Path("/app/data/scan_targets")
|
||||
|
||||
@staticmethod
|
||||
def _read_yaml(path: Path) -> Dict[str, Any]:
|
||||
@@ -144,6 +149,7 @@ class ScanConfigRepository:
|
||||
# Parse scan_options
|
||||
so_raw = data.get("scan_options", {}) or {}
|
||||
scan_options = ScanOptions(
|
||||
cron=self._validate_cron_or_none(so_raw.get("cron")),
|
||||
udp_scan=bool(so_raw.get("udp_scan", False)),
|
||||
tls_security_scan=bool(so_raw.get("tls_security_scan", True)),
|
||||
tls_exp_check=bool(so_raw.get("tls_exp_check", True)),
|
||||
@@ -155,6 +161,8 @@ class ScanConfigRepository:
|
||||
report_name=str(rep_raw.get("report_name", "Scan Report")),
|
||||
report_filename=str(rep_raw.get("report_filename", "report.html")),
|
||||
full_details=bool(rep_raw.get("full_details", False)),
|
||||
email_to=self._as_str_list(rep_raw.get("email_to", []), "email_to"),
|
||||
email_cc=self._as_str_list(rep_raw.get("email_cc", []), "email_cc"),
|
||||
)
|
||||
|
||||
# Parse targets
|
||||
@@ -179,6 +187,20 @@ class ScanConfigRepository:
|
||||
scan_targets=targets,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_cron_or_none(expr: Optional[str]) -> Optional[str]:
|
||||
"""
|
||||
Validate a standard 5-field crontab string via CronTrigger.from_crontab.
|
||||
Return the original string if valid; None if empty/None.
|
||||
Raise ValueError on invalid expressions.
|
||||
"""
|
||||
if not expr:
|
||||
return None
|
||||
expr = str(expr).strip()
|
||||
# Validate now so we fail early on bad configs
|
||||
CronTrigger.from_crontab(expr) # will raise on invalid
|
||||
return expr
|
||||
|
||||
@staticmethod
|
||||
def _validate_config(cfg: ScanConfigFile, source: str) -> None:
|
||||
"""
|
||||
@@ -192,7 +214,27 @@ class ScanConfigRepository:
|
||||
if dups:
|
||||
raise ValueError(f"{source}: duplicate IP(s) in scan_targets: {dups}")
|
||||
|
||||
# Optional helpers
|
||||
@staticmethod
|
||||
def _as_str_list(value: Any, field_name: str) -> List[str]:
|
||||
"""
|
||||
Accept a single string or a list of strings; return List[str].
|
||||
"""
|
||||
if value is None or value == []:
|
||||
return []
|
||||
if isinstance(value, str):
|
||||
return [value.strip()] if value.strip() else []
|
||||
if isinstance(value, (list, tuple)):
|
||||
out: List[str] = []
|
||||
for v in value:
|
||||
if not isinstance(v, str):
|
||||
raise TypeError(f"'{field_name}' must contain only strings.")
|
||||
s = v.strip()
|
||||
if s:
|
||||
out.append(s)
|
||||
return out
|
||||
raise TypeError(f"'{field_name}' must be a string or a list of strings.")
|
||||
|
||||
# helpers
|
||||
|
||||
def list_configs(self) -> List[str]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user