forked from 0xWheatyz/SPARC
b32eebff8a
Uncomment the ruff check and pytest steps in the Gitea Actions build workflow so that linting violations and test failures block image builds. Fix all pre-existing ruff violations (E402 import ordering in analyzer.py, F821 undefined name in api.py, I001 unsorted imports in test files, F401 unused import in test_rate_limit.py). Closes leeworks-agents/SPARC#1559 Closes leeworks-agents/SPARC#1560 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
124 lines
4.7 KiB
Python
124 lines
4.7 KiB
Python
"""Tests for security hardening: JWT secret startup check, CORS config, credential handling."""
|
|
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
|
|
class TestJWTSecretStartupCheck:
|
|
"""Test the startup guard that refuses default JWT secret in non-dev environments."""
|
|
|
|
def test_default_secret_in_production_raises(self):
|
|
"""Starting with default secret and APP_ENV=production must raise RuntimeError."""
|
|
with patch.dict(os.environ, {"APP_ENV": "production"}):
|
|
# Reload config to pick up the new APP_ENV
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
|
|
from SPARC.auth import _DEFAULT_JWT_SECRET, check_jwt_secret
|
|
# Patch JWT_SECRET to the default
|
|
with patch("SPARC.auth.JWT_SECRET", _DEFAULT_JWT_SECRET):
|
|
with pytest.raises(RuntimeError, match="FATAL.*JWT_SECRET"):
|
|
check_jwt_secret()
|
|
|
|
# Restore config
|
|
with patch.dict(os.environ, {"APP_ENV": "development"}):
|
|
importlib.reload(SPARC.config)
|
|
|
|
def test_default_secret_in_development_succeeds(self):
|
|
"""Starting with default secret and APP_ENV=development must not raise."""
|
|
with patch.dict(os.environ, {"APP_ENV": "development"}):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
|
|
from SPARC.auth import _DEFAULT_JWT_SECRET, check_jwt_secret
|
|
with patch("SPARC.auth.JWT_SECRET", _DEFAULT_JWT_SECRET):
|
|
# Should not raise
|
|
check_jwt_secret()
|
|
|
|
# Restore
|
|
importlib.reload(SPARC.config)
|
|
|
|
def test_custom_secret_in_production_succeeds(self):
|
|
"""Starting with a custom secret in production must not raise."""
|
|
with patch.dict(os.environ, {"APP_ENV": "production"}):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
|
|
from SPARC.auth import check_jwt_secret
|
|
with patch("SPARC.auth.JWT_SECRET", "my-secure-random-secret-abc123"):
|
|
# Should not raise
|
|
check_jwt_secret()
|
|
|
|
with patch.dict(os.environ, {"APP_ENV": "development"}):
|
|
importlib.reload(SPARC.config)
|
|
|
|
def test_default_secret_unset_env_succeeds(self):
|
|
"""When APP_ENV is unset (defaults to development), default secret is allowed."""
|
|
with patch.dict(os.environ, {}, clear=False):
|
|
# Remove APP_ENV if present
|
|
env = os.environ.copy()
|
|
env.pop("APP_ENV", None)
|
|
with patch.dict(os.environ, env, clear=True):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
|
|
from SPARC.auth import _DEFAULT_JWT_SECRET, check_jwt_secret
|
|
with patch("SPARC.auth.JWT_SECRET", _DEFAULT_JWT_SECRET):
|
|
# Should not raise (defaults to development)
|
|
check_jwt_secret()
|
|
|
|
with patch.dict(os.environ, {"APP_ENV": "development"}):
|
|
importlib.reload(SPARC.config)
|
|
|
|
|
|
class TestCORSConfig:
|
|
"""Test that CORS origins are configurable via environment variable."""
|
|
|
|
def test_default_cors_origins(self):
|
|
"""When CORS_ORIGINS is unset, defaults to localhost origins."""
|
|
with patch.dict(os.environ, {"CORS_ORIGINS": ""}):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
assert SPARC.config.cors_origins == [
|
|
"http://localhost:3000",
|
|
"http://localhost:5173",
|
|
]
|
|
|
|
def test_custom_cors_origins(self):
|
|
"""Setting CORS_ORIGINS configures allowed origins."""
|
|
with patch.dict(os.environ, {"CORS_ORIGINS": "https://sparc.example.com,https://app.example.com"}):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
assert SPARC.config.cors_origins == [
|
|
"https://sparc.example.com",
|
|
"https://app.example.com",
|
|
]
|
|
# Restore
|
|
with patch.dict(os.environ, {"CORS_ORIGINS": ""}):
|
|
importlib.reload(SPARC.config)
|
|
|
|
def test_single_cors_origin(self):
|
|
"""A single origin without comma works correctly."""
|
|
with patch.dict(os.environ, {"CORS_ORIGINS": "https://sparc.example.com"}):
|
|
import importlib
|
|
|
|
import SPARC.config
|
|
importlib.reload(SPARC.config)
|
|
assert SPARC.config.cors_origins == ["https://sparc.example.com"]
|
|
with patch.dict(os.environ, {"CORS_ORIGINS": ""}):
|
|
importlib.reload(SPARC.config)
|