"""Email notification service using SMTP. Sending is silently skipped when SMTP_ENABLED=False (default). All errors are caught and logged — email failures never crash the caller. """ import logging import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from app.config import settings logger = logging.getLogger(__name__) def send_email(to: str, subject: str, html_body: str) -> bool: """Send an HTML email. Returns True on success, False on skip/error.""" if not settings.SMTP_ENABLED: logger.debug("SMTP disabled — skipping email to %s: %s", to, subject) return False if not to: return False try: msg = MIMEMultipart("alternative") msg["Subject"] = f"[Aegis] {subject}" msg["From"] = settings.SMTP_FROM_EMAIL msg["To"] = to msg.attach(MIMEText(html_body, "html")) with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT, timeout=10) as server: if settings.SMTP_USE_TLS: server.starttls() if settings.SMTP_USERNAME: server.login(settings.SMTP_USERNAME, settings.SMTP_PASSWORD) server.send_message(msg) logger.info("Email sent to %s: %s", to, subject) return True except Exception: logger.exception("Failed to send email to %s: %s", to, subject) return False def send_test_validated_email(to: str, test_name: str, technique_id: str, test_id: str) -> bool: """Notify that a test was validated.""" url = f"{settings.PLATFORM_URL}/tests/{test_id}" html = f"""

✅ Test Validated

Test {test_name} for technique {technique_id} has been validated.

View Test

Aegis ATT&CK Coverage Platform

""" return send_email(to, f"Test Validated: {test_name}", html) def send_campaign_completed_email(to: str, campaign_name: str, campaign_id: str) -> bool: """Notify that a campaign was completed.""" url = f"{settings.PLATFORM_URL}/campaigns/{campaign_id}" html = f"""

🎯 Campaign Completed

Campaign {campaign_name} has been completed.

View Campaign

Aegis ATT&CK Coverage Platform

""" return send_email(to, f"Campaign Completed: {campaign_name}", html) def send_new_mitre_techniques_email(to: str, created: int, updated: int) -> bool: """Notify of new MITRE techniques after sync.""" if created == 0: return False html = f"""

🔄 MITRE ATT&CK Updated

{created} new techniques added, {updated} updated.

View Techniques

Aegis ATT&CK Coverage Platform

""" return send_email(to, f"MITRE ATT&CK Updated: {created} new techniques", html)