From b8bc4ea21f3465c267a86f80d1e129b03149c081 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 29 Apr 2026 21:40:10 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20email=5Fsend=20allowlist=20=E2=80=94=20?= =?UTF-8?q?block=20sends=20to=20non-whitelisted=20addresses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reads home/{username}/email_allowlist.json (JSON array of addresses). Fails safe: if file is missing or address not listed, send is blocked with an informative message. home/ is gitignored; create the file manually per user. Co-Authored-By: Claude Sonnet 4.6 --- cortex/tools/notify.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cortex/tools/notify.py b/cortex/tools/notify.py index 779844f..dfcf916 100644 --- a/cortex/tools/notify.py +++ b/cortex/tools/notify.py @@ -6,15 +6,40 @@ email_send uses the server SMTP config from .env (smtp_server, smtp_from_*). """ import asyncio +import json import logging +from config import settings from persona import get_user logger = logging.getLogger(__name__) +def _email_allowlist(username: str) -> list[str]: + """Load the per-user email allowlist. Returns empty list if not configured.""" + path = settings.home_root() / username / "email_allowlist.json" + try: + return [a.lower().strip() for a in json.loads(path.read_text())] + except FileNotFoundError: + return [] + except Exception as e: + logger.warning("failed to read email_allowlist.json for %s: %s", username, e) + return [] + + async def email_send(to: str, subject: str, body: str) -> str: """Send an email via the server's configured SMTP account.""" + username = get_user() + allowlist = _email_allowlist(username) + + if not allowlist: + return ( + "Email blocked — no allowlist configured. " + f"Add allowed addresses to home/{username}/email_allowlist.json as a JSON array." + ) + if to.lower().strip() not in allowlist: + return f"Email blocked — {to} is not in the allowlist for {username}." + from email_utils import send_email ok = await asyncio.to_thread( send_email,