diff --git a/src/config.py b/src/config.py index 3750e88a..71f7c04c 100644 --- a/src/config.py +++ b/src/config.py @@ -469,6 +469,12 @@ def validate_startup(*, force: bool = False) -> None: ", ".join(_missing), ) sys.exit(1) + if "*" in settings.cors_origins: + _startup_logger.error( + "PRODUCTION SECURITY ERROR: Wildcard '*' in CORS_ORIGINS is not " + "allowed in production — set explicit origins via CORS_ORIGINS env var." + ) + sys.exit(1) _startup_logger.info("Production mode: security secrets validated ✓") else: if not settings.l402_hmac_secret: diff --git a/tests/test_lazy_init.py b/tests/test_lazy_init.py index 3e881733..70e148b4 100644 --- a/tests/test_lazy_init.py +++ b/tests/test_lazy_init.py @@ -37,6 +37,19 @@ class TestConfigLazyValidation: ): validate_startup(force=True) + def test_validate_startup_exits_on_cors_wildcard_in_production(self): + """validate_startup() should exit in production when CORS has wildcard.""" + from config import settings, validate_startup + + with ( + patch.object(settings, "timmy_env", "production"), + patch.object(settings, "l402_hmac_secret", "test-secret-hex-value-32"), + patch.object(settings, "l402_macaroon_secret", "test-macaroon-hex-value-32"), + patch.object(settings, "cors_origins", ["*"]), + pytest.raises(SystemExit), + ): + validate_startup(force=True) + def test_validate_startup_ok_with_secrets(self): """validate_startup() should not exit when secrets are set.""" from config import settings, validate_startup