fix:批量修复了一些问题
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"""单测:生产环境启动安全校验。
|
||||
|
||||
覆盖:
|
||||
- production 缺 SECRET_KEY → 阻断(sys.exit)
|
||||
- production 缺鉴权 → 阻断
|
||||
- production 全配置 → 通过
|
||||
- development 缺配置 → 仅警告(不退出)
|
||||
- DATABASE_URL 弱密码 → production 阻断 / development 仅警告
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.core.config import Settings
|
||||
from src.core.security_check import (
|
||||
_WEAK_SECRET_KEYS,
|
||||
_WEAK_DB_PATTERNS,
|
||||
assert_security_on_startup,
|
||||
validate_security,
|
||||
)
|
||||
|
||||
|
||||
def _base_settings(**overrides) -> Settings:
|
||||
"""构造测试用 Settings,默认模拟一个"已合规"的基线。"""
|
||||
defaults = dict(
|
||||
APP_ENV="production",
|
||||
SECRET_KEY="aSwLuw2mqoQdSKUfB3eFVfW2Tv7VnJRRixMxOwZZi5M=",
|
||||
ADMIN_PASSWORD="",
|
||||
ADMIN_API_KEY="",
|
||||
DATABASE_URL="postgresql+asyncpg://user:StrongP@ssw0rd@db:5432/prod",
|
||||
REQUIRE_ADMIN_AUTH=False,
|
||||
)
|
||||
defaults.update(overrides)
|
||||
return Settings(**defaults)
|
||||
|
||||
|
||||
class TestValidateSecurity:
|
||||
"""validate_security 逻辑。"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_fully_configured_passes(self):
|
||||
with patch("src.core.security_check.settings", _base_settings()), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
result = await validate_security()
|
||||
assert result["ok"] is True
|
||||
assert result["errors"] == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_missing_secret_key_fails(self):
|
||||
with patch("src.core.security_check.settings", _base_settings(SECRET_KEY="")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
result = await validate_security()
|
||||
assert result["ok"] is False
|
||||
assert any("SECRET_KEY" in e for e in result["errors"])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_weak_secret_key_fails(self):
|
||||
with patch("src.core.security_check.settings", _base_settings(SECRET_KEY="changeme")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
result = await validate_security()
|
||||
assert result["ok"] is False
|
||||
assert any("SECRET_KEY" in e for e in result["errors"])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_missing_auth_fails(self):
|
||||
with patch("src.core.security_check.settings", _base_settings()), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=False)):
|
||||
result = await validate_security()
|
||||
assert result["ok"] is False
|
||||
assert any("鉴权" in e or "ADMIN" in e for e in result["errors"])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_weak_db_password_fails(self):
|
||||
with patch("src.core.security_check.settings",
|
||||
_base_settings(DATABASE_URL="postgresql+asyncpg://football:football@localhost:5432/football")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
result = await validate_security()
|
||||
assert result["ok"] is False
|
||||
assert any("DATABASE_URL" in e or "弱密码" in e for e in result["errors"])
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_development_missing_config_only_warns(self):
|
||||
"""development:缺 SECRET_KEY/鉴权 → errors 存在但弱 DB 密码不进 errors。"""
|
||||
with patch("src.core.security_check.settings",
|
||||
_base_settings(APP_ENV="development", SECRET_KEY="", ADMIN_API_KEY="",
|
||||
DATABASE_URL="postgresql+asyncpg://football:football@localhost:5432/football")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=False)):
|
||||
result = await validate_security()
|
||||
# development 下:弱 DB 密码只在 warnings,不会升级到 errors
|
||||
assert result["ok"] is False # 仍有 errors(缺密钥 + 缺鉴权)
|
||||
assert any("DATABASE_URL" in w for w in result["warnings"])
|
||||
|
||||
|
||||
class TestAssertSecurityOnStartup:
|
||||
"""assert_security_on_startup 退出行为。"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_failure_exits(self):
|
||||
with patch("src.core.security_check.settings", _base_settings(SECRET_KEY="")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
await assert_security_on_startup()
|
||||
assert exc.value.code == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_production_pass_does_not_exit(self):
|
||||
with patch("src.core.security_check.settings", _base_settings()), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
# 不应抛异常 / 退出
|
||||
await assert_security_on_startup()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_development_failure_does_not_exit(self):
|
||||
with patch("src.core.security_check.settings",
|
||||
_base_settings(APP_ENV="development", SECRET_KEY="")), \
|
||||
patch("src.core.security_check._auth_configured", AsyncMock(return_value=True)):
|
||||
# development 即使有问题也不退出
|
||||
await assert_security_on_startup()
|
||||
|
||||
|
||||
def test_weak_secret_keys_list_not_empty():
|
||||
"""防御性:弱密钥表应包含常见弱值。"""
|
||||
assert "changeme" in _WEAK_SECRET_KEYS
|
||||
assert "football:football@" in _WEAK_DB_PATTERNS
|
||||
Reference in New Issue
Block a user