Files
gt-ai-os-community/apps/control-panel-backend/app/main.py
HackWeasel b9dfb86260 GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts:

- Fix stack trace exposure in error responses
- Add SSRF protection with DNS resolution checking
- Implement proper URL hostname validation (replaces substring matching)
- Add centralized path sanitization to prevent path traversal
- Fix ReDoS vulnerability in email validation regex
- Improve HTML sanitization in validation utilities
- Fix capability wildcard matching in auth utilities
- Update glob dependency to address CVE
- Add CodeQL suppression comments for verified false positives

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 17:04:45 -05:00

209 lines
6.9 KiB
Python

"""
GT 2.0 Control Panel Backend - FastAPI Application
"""
import warnings
# Suppress passlib's bcrypt version detection warning (cosmetic only, doesn't affect functionality)
# passlib 1.7.4 tries to read bcrypt.__about__.__version__ which was removed in bcrypt 4.1.x
warnings.filterwarnings("ignore", message=".*module 'bcrypt' has no attribute '__about__'.*")
import logging
import structlog
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import time
from app.core.config import settings
from app.core.database import engine, init_db
from app.core.api_standards import setup_api_standards
from app.api import auth, resources, tenants, users, tfa, public
from app.api.v1 import api_keys, analytics, resource_management, models, tenant_models, templates, system
from app.api.internal import api_keys as internal_api_keys
from app.api.internal import optics as internal_optics
from app.api.internal import sessions as internal_sessions
from app.middleware.session_validation import SessionValidationMiddleware
# Configure structured logging
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer()
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
logger = structlog.get_logger()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan events"""
# Startup
logger.info("Starting GT 2.0 Control Panel Backend")
# Initialize database
await init_db()
logger.info("Database initialized")
yield
# Shutdown
logger.info("Shutting down GT 2.0 Control Panel Backend")
# Create FastAPI application
app = FastAPI(
title="GT 2.0 Control Panel API",
description="Enterprise AI as a Service Platform - Control Panel Backend",
version="1.0.0",
docs_url="/docs" if settings.ENVIRONMENT != "production" else None,
redoc_url="/redoc" if settings.ENVIRONMENT != "production" else None,
lifespan=lifespan
)
# Setup CB-REST API standards (adds middleware)
setup_api_standards(app, settings.SECRET_KEY)
# Add CORS middleware (must be added after CB-REST middleware)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
expose_headers=["X-Session-Warning", "X-Session-Expired"], # Issue #264: Expose session headers to frontend
)
# Add session validation middleware (Issue #264: OWASP/NIST compliant session management)
app.add_middleware(SessionValidationMiddleware)
# Security headers middleware (production only)
@app.middleware("http")
async def security_headers_middleware(request: Request, call_next):
response = await call_next(request)
if settings.ENVIRONMENT == "production":
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
# Middleware for request logging
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
start_time = time.time()
# Process request
response = await call_next(request)
# Calculate duration
duration = time.time() - start_time
# Log request
logger.info(
"Request processed",
method=request.method,
path=request.url.path,
status_code=response.status_code,
duration=duration,
user_agent=request.headers.get("user-agent"),
client_ip=request.client.host if request.client else None
)
return response
# Global exception handler
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.error(
"Unhandled exception",
path=request.url.path,
method=request.method,
error=str(exc),
exc_info=True
)
return JSONResponse(
status_code=500,
content={
"success": False,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error"
}
}
)
# Health check endpoints
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "service": "gt2-control-panel-backend"}
@app.get("/ready")
async def readiness_check():
"""Readiness check endpoint"""
try:
# Check database connection
from app.core.database import get_db_session
async with get_db_session() as session:
await session.execute("SELECT 1")
return {"status": "ready", "service": "gt2-control-panel-backend"}
except Exception as e:
logger.error("Readiness check failed", error=str(e))
return JSONResponse(
status_code=503,
content={"status": "not ready", "error": "Database connection failed"}
)
# Include API routers
app.include_router(auth.router, prefix="/api/v1", tags=["Authentication"])
app.include_router(tfa.router, prefix="/api/v1", tags=["Two-Factor Authentication"])
app.include_router(public.router, prefix="/api/v1", tags=["Public"])
app.include_router(tenants.router, prefix="/api/v1", tags=["Tenants"])
app.include_router(users.router, prefix="/api/v1", tags=["Users"])
app.include_router(resources.router, prefix="/api/v1", tags=["AI Resources"])
# V1 API routes
app.include_router(api_keys.router, tags=["API Keys"])
app.include_router(analytics.router, tags=["Analytics"])
app.include_router(resource_management.router, prefix="/api/v1", tags=["Resource Management"])
app.include_router(models.router, prefix="/api/v1", tags=["Model Management"])
app.include_router(tenant_models.router, prefix="/api/v1", tags=["Tenant Model Management"])
app.include_router(tenant_models.router, prefix="/api/v1/tenant-models", tags=["Tenant Model Access"])
app.include_router(templates.router, tags=["Templates"])
app.include_router(system.router, tags=["System Management"])
# Internal service-to-service routes
app.include_router(internal_api_keys.router, tags=["Internal"])
app.include_router(internal_optics.router, tags=["Internal"])
app.include_router(internal_sessions.router, tags=["Internal"])
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=8001,
reload=settings.DEBUG,
log_level="info"
)