Spaces:
Sleeping
Sleeping
File size: 2,510 Bytes
50dca14 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | """
Flask middleware:
- Request timing
- Security headers (XSS, clickjacking, content-type sniffing)
- Global error handlers (404, 429, 500)
"""
from __future__ import annotations
import logging
import time
from flask import Flask, jsonify, request, render_template
logger = logging.getLogger(__name__)
def register_middleware(app: Flask) -> None:
"""Register all middleware and error handlers on the Flask app."""
@app.before_request
def start_timer() -> None:
request._start_time = time.monotonic()
@app.after_request
def add_security_headers(response):
duration = time.monotonic() - getattr(request, "_start_time", time.monotonic())
response.headers["X-Response-Time"] = f"{duration * 1000:.2f}ms"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "SAMEORIGIN"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Permissions-Policy"] = "geolocation=(), microphone=()"
if app.config.get("SESSION_COOKIE_SECURE"):
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
logger.debug(
"%s %s -> %d (%.2fms)",
request.method,
request.path,
response.status_code,
duration * 1000,
)
return response
@app.errorhandler(400)
def bad_request(exc):
if request.is_json:
return jsonify({"error": "Bad request", "detail": str(exc)}), 400
return render_template("pages/error.html", code=400, message="Bad Request"), 400
@app.errorhandler(404)
def not_found(exc):
if request.is_json:
return jsonify({"error": "Not found"}), 404
return render_template("pages/error.html", code=404, message="Page Not Found"), 404
@app.errorhandler(429)
def rate_limited(exc):
if request.is_json:
return jsonify({"error": "Rate limit exceeded. Please slow down."}), 429
return render_template("pages/error.html", code=429, message="Rate limit exceeded"), 429
@app.errorhandler(500)
def internal_error(exc):
logger.exception("Internal server error: %s", exc)
if request.is_json:
return jsonify({"error": "Internal server error"}), 500
return render_template("pages/error.html", code=500, message="Internal Server Error"), 500
|