"""Fail-fast checks for insecure production defaults.""" from __future__ import annotations import logging from urllib.parse import urlparse from app.config import Settings logger = logging.getLogger("sac") _WEAK_JWT_SECRETS = frozenset( { "", "change-me-in-production", "change-me-openssl-rand-hex-32", "CHANGE_ME_openssl_rand_hex_32", } ) def _is_local_dev_url(public_url: str) -> bool: parsed = urlparse(public_url.strip() or "http://localhost:8000") host = (parsed.hostname or "").lower() return host in {"localhost", "127.0.0.1", "::1", "testserver"} def validate_security_settings(settings: Settings) -> None: """Raise on dangerous defaults when SAC_SECURITY_ENFORCE=true.""" if not settings.sac_security_enforce: return if settings.jwt_secret in _WEAK_JWT_SECRETS: raise RuntimeError( "JWT_SECRET is missing or uses an insecure default. " "Set a random value in sac-api.env (openssl rand -hex 32)." ) if settings.cors_origins.strip() == "*" and not _is_local_dev_url(settings.sac_public_url): raise RuntimeError( "CORS_ORIGINS=* is not allowed with SAC_SECURITY_ENFORCE=true on non-local SAC_PUBLIC_URL. " "Set CORS_ORIGINS to your UI origin, e.g. https://sac.example.com" ) def warn_relaxed_security(settings: Settings) -> None: if settings.sac_security_enforce: return if settings.jwt_secret in _WEAK_JWT_SECRETS: logger.warning("JWT_SECRET uses insecure default — set a strong secret before production") if settings.cors_origins.strip() == "*": logger.warning("CORS_ORIGINS=* — restrict to explicit origins in production")