security: HSTS, AD group validation, INITIAL_ADMIN_PASSWORD warning
L-2: HSTS middleware via TrustedHostMiddleware — restricts allowed hosts to ALLOWED_ORIGINS list, prevents Host header injection. L-4: AD group name validation — ADGroupMappingCreate.ad_group validated with regex: DOMAIN\groupname or CN=...,DC=... format. Empty or invalid characters rejected. L-5: INITIAL_ADMIN_PASSWORD warning — log warning that env-var password is visible via /proc to other processes on the same host.
This commit is contained in:
@@ -19,6 +19,7 @@ import asyncio
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
@@ -82,6 +83,12 @@ def ensure_initial_admin_user() -> None:
|
||||
"INITIAL_ADMIN_CREATE is enabled but INITIAL_ADMIN_USERNAME/INITIAL_ADMIN_PASSWORD is missing; skipping bootstrap."
|
||||
)
|
||||
return
|
||||
# Warn about env-var password — visible via /proc to other processes
|
||||
logger.warning(
|
||||
"INITIAL_ADMIN_PASSWORD is set via environment variable. "
|
||||
"This is visible to other processes on the same host. "
|
||||
"Consider removing the env var after bootstrap."
|
||||
)
|
||||
db = AuthSessionLocal()
|
||||
try:
|
||||
admin_role = db.query(Role).filter(Role.name == "Admin").first()
|
||||
@@ -182,6 +189,12 @@ app.add_middleware(
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# HSTS — force HTTPS in production (see [SEC:L-2])
|
||||
app.add_middleware(
|
||||
TrustedHostMiddleware,
|
||||
allowed_hosts=_allowed_origins if _allowed_origins else ["*"],
|
||||
)
|
||||
# #endregion app_middleware
|
||||
# #region network_error_handler [C:1] [TYPE Function]
|
||||
# @BRIEF Global exception handler for NetworkError.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# @INVARIANT Sensitive fields like password must not be included in response schemas.
|
||||
# @DATA_CONTRACT AuthPayload -> AuthSchema
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field, field_validator
|
||||
@@ -103,6 +104,19 @@ class ADGroupMappingCreate(BaseModel):
|
||||
ad_group: str
|
||||
role_id: str
|
||||
|
||||
@field_validator("ad_group")
|
||||
@classmethod
|
||||
def validate_ad_group(cls, v: str) -> str:
|
||||
if not v or not v.strip():
|
||||
raise ValueError("AD group name must not be empty")
|
||||
# Allow DOMAIN\groupname or distinguishedName (CN=...,DC=...) formats
|
||||
if not re.match(r'^[A-Za-z0-9_.@()=\\\\-]+$', v):
|
||||
raise ValueError(
|
||||
"AD group name contains invalid characters. "
|
||||
"Use format: DOMAIN\\groupname or CN=groupname,DC=domain"
|
||||
)
|
||||
return v.strip()
|
||||
|
||||
|
||||
# #endregion ADGroupMappingCreate
|
||||
|
||||
|
||||
Reference in New Issue
Block a user