|
|
import time |
|
|
from typing import Any |
|
|
|
|
|
from django.utils.deprecation import MiddlewareMixin |
|
|
from django.http import HttpRequest, HttpResponse |
|
|
from .models import AuditLog |
|
|
|
|
|
class SecurityHeadersMiddleware(MiddlewareMixin): |
|
|
def process_response(self, request: HttpRequest, response: HttpResponse): |
|
|
response.headers.setdefault("X-Content-Type-Options", "nosniff") |
|
|
response.headers.setdefault("Referrer-Policy", "no-referrer-when-downgrade") |
|
|
response.headers.setdefault("X-Frame-Options", "SAMEORIGIN") |
|
|
|
|
|
response.headers.setdefault("Content-Security-Policy", "default-src 'self'; img-src 'self' data:;") |
|
|
return response |
|
|
|
|
|
class AuditLogMiddleware(MiddlewareMixin): |
|
|
def process_request(self, request: HttpRequest): |
|
|
request._audit_start = time.perf_counter() |
|
|
|
|
|
def process_response(self, request: HttpRequest, response: HttpResponse): |
|
|
try: |
|
|
path = request.path[:300] |
|
|
query = request.META.get("QUERY_STRING", "")[:500] |
|
|
ua = request.META.get("HTTP_USER_AGENT", "")[:300] |
|
|
ip = request.META.get("REMOTE_ADDR") |
|
|
latency_ms = None |
|
|
start = getattr(request, "_audit_start", None) |
|
|
if start is not None: |
|
|
latency_ms = (time.perf_counter() - start) * 1000 |
|
|
|
|
|
intent = "" |
|
|
confidence = None |
|
|
data: Any = getattr(response, "data", None) |
|
|
if isinstance(data, dict): |
|
|
intent = str(data.get("intent") or "")[:50] |
|
|
confidence_value = data.get("confidence") |
|
|
try: |
|
|
confidence = float(confidence_value) if confidence_value is not None else None |
|
|
except (TypeError, ValueError): |
|
|
confidence = None |
|
|
|
|
|
AuditLog.objects.create( |
|
|
path=path, |
|
|
query=query, |
|
|
user_agent=ua, |
|
|
ip=ip, |
|
|
status=response.status_code, |
|
|
intent=intent, |
|
|
confidence=confidence, |
|
|
latency_ms=latency_ms, |
|
|
) |
|
|
except Exception: |
|
|
|
|
|
pass |
|
|
return response |
|
|
|
|
|
|