Align the project baseline with the latest admin interface styling and layout structure while documenting setup and usage updates in README.
95 lines
3.6 KiB
Python
95 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
from urllib.parse import parse_qs, urlparse
|
|
|
|
from app.core.config import settings
|
|
from app.core.install_secrets import read_install_secrets
|
|
from app.core.password_denylist import load_denylist
|
|
from app.core.redis import get_redis_client
|
|
from app.core.database import session_scope
|
|
from app.modules.auth.models import RefreshToken
|
|
from app.modules.users import repository
|
|
from sqlalchemy import func, select
|
|
|
|
|
|
def build_security_diagnostics_report() -> dict:
|
|
redis_client = get_redis_client()
|
|
db = urlparse(settings.database_url)
|
|
sslmode = parse_qs(db.query).get("sslmode", [""])[0]
|
|
install_secrets = read_install_secrets()
|
|
with session_scope() as session:
|
|
refresh_count = session.scalar(select(func.count()).select_from(RefreshToken)) or 0
|
|
checks = [
|
|
{
|
|
"id": "jwt_access_secret",
|
|
"status": "ok" if len(settings.jwt_access_secret) >= 32 else "fail",
|
|
"message": "JWT access secret configured",
|
|
},
|
|
{
|
|
"id": "jwt_refresh_pepper",
|
|
"status": "ok" if len(settings.jwt_refresh_pepper) >= 32 else "fail",
|
|
"message": "JWT refresh pepper configured",
|
|
},
|
|
{
|
|
"id": "cookie_secure",
|
|
"status": "ok" if settings.cookie_secure else "warn",
|
|
"message": "Refresh cookie Secure flag is enabled",
|
|
},
|
|
{
|
|
"id": "rate_limit_backend",
|
|
"status": "ok" if redis_client is not None else "warn",
|
|
"message": "Rate limiter uses Redis backend",
|
|
},
|
|
{
|
|
"id": "password_denylist",
|
|
"status": "ok" if len(load_denylist()) >= 1000 else "warn",
|
|
"message": "Password denylist has strong coverage",
|
|
},
|
|
{
|
|
"id": "cors_wildcard",
|
|
"status": "ok" if "*" not in settings.cors_origins else "fail",
|
|
"message": "CORS does not include wildcard origin",
|
|
},
|
|
{
|
|
"id": "superuser_exists",
|
|
"status": "ok" if repository.count_superusers() > 0 else "fail",
|
|
"message": "At least one superuser exists",
|
|
},
|
|
{
|
|
"id": "docs_production",
|
|
"status": "warn" if settings.enable_docs else "ok",
|
|
"message": "API docs are disabled in production",
|
|
},
|
|
{
|
|
"id": "db_default_credentials",
|
|
"status": "fail" if db.username == "user" and db.password == "pass" else "ok",
|
|
"message": "Database does not use default credentials",
|
|
},
|
|
{
|
|
"id": "db_localhost_exposed",
|
|
"status": "fail" if settings.app_env.lower() == "production" and db.hostname in {"localhost", "127.0.0.1"} else "ok",
|
|
"message": "Production database host is not localhost",
|
|
},
|
|
{
|
|
"id": "db_ssl_mode",
|
|
"status": "ok" if settings.app_env.lower() != "production" or sslmode == "require" else "warn",
|
|
"message": "Production database URL uses sslmode=require",
|
|
},
|
|
{
|
|
"id": "db_refresh_token_table_size",
|
|
"status": "warn" if refresh_count > 10000 else "ok",
|
|
"message": "Refresh token table size is under threshold",
|
|
},
|
|
{
|
|
"id": "install_secrets_initialized",
|
|
"status": "ok" if bool(install_secrets) else "fail",
|
|
"message": "Install secrets file is initialized",
|
|
},
|
|
{
|
|
"id": "install_secrets_locked",
|
|
"status": "ok" if install_secrets.get("SECRETS_LOCKED") == "true" else "warn",
|
|
"message": "Install secrets are locked after bootstrap",
|
|
},
|
|
]
|
|
return {"checks": checks}
|