from __future__ import annotations from dataclasses import dataclass, field from typing import Any from .security import ( IRCSecurityConfig, _find_mutable_allowlist_entries, ) @dataclass class DoctorDiagnosis: status: str = "ok" warnings: list[str] = field(default_factory=list) errors: list[str] = field(default_factory=list) checks: list[dict[str, Any]] = field(default_factory=list) def diagnose( config: dict[str, Any], security_config: IRCSecurityConfig | None = None, use_tls: bool = True, nickserv_enabled: bool = False, nickserv_register: bool = False, ) -> DoctorDiagnosis: result = DoctorDiagnosis() _check_tls(result, use_tls) _check_nickserv(result, nickserv_enabled, nickserv_register) if security_config: _check_policies(result, security_config) _check_mutable_allowlist(result, security_config) return result def _check_tls(result: DoctorDiagnosis, use_tls: bool) -> None: check = {"name": "tls", "status": "ok"} if not use_tls: check["status"] = "warning" check["message"] = "TLS is disabled — connection is not encrypted" result.warnings.append(check["message"]) result.checks.append(check) def _check_nickserv( result: DoctorDiagnosis, nickserv_enabled: bool, nickserv_register: bool, ) -> None: check = {"name": "nickserv", "status": "ok"} if not nickserv_enabled: check["status"] = "warning" check["message"] = "NickServ is not enabled — nick may not be reserved" result.warnings.append(check["message"]) elif nickserv_register: check["status"] = "warning" check["message"] = ( "nickserv.register is enabled — after first successful registration " "consider removing this config to avoid repeated REGISTER commands" ) result.warnings.append(check["message"]) result.checks.append(check) def _check_policies( result: DoctorDiagnosis, security_config: IRCSecurityConfig, ) -> None: check = {"name": "group_policy", "status": "ok"} if security_config.group_policy == "open": check["status"] = "warning" check["message"] = "Group policy is 'open' — anyone can trigger bot in channels" result.warnings.append(check["message"]) result.checks.append(check) def _check_mutable_allowlist( result: DoctorDiagnosis, security_config: IRCSecurityConfig, ) -> None: mutable_entries = _find_mutable_allowlist_entries(security_config) for entry in mutable_entries: msg = f"Mutable allowlist entry detected: '{entry}' — consider using nick!user@host format" result.warnings.append(msg) result.checks.append({"name": "mutable_allowlist", "status": "warning", "message": msg})