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>
This commit is contained in:
209
apps/control-panel-backend/app/main.py
Normal file
209
apps/control-panel-backend/app/main.py
Normal file
@@ -0,0 +1,209 @@
|
||||
"""
|
||||
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"
|
||||
)
|
||||
Reference in New Issue
Block a user