From bd9ba9b52651ddffc95a77e264b3bf0c261604d0 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 9 Mar 2026 18:28:25 -0400 Subject: [PATCH] Fix scanner probes returning 200 via SPA catch-all The 404 handler was unconditionally serving index.html (200) for all non-API, non-static paths, including scanner probes for .php, .asp, .cgi etc. Added _SPA_EXTENSIONS set so paths with unknown extensions get a real 404 instead of a misleading 200. Co-Authored-By: Claude Sonnet 4.6 --- src/fabledassistant/app.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/fabledassistant/app.py b/src/fabledassistant/app.py index b823292..53d4585 100644 --- a/src/fabledassistant/app.py +++ b/src/fabledassistant/app.py @@ -238,17 +238,34 @@ def create_app() -> Quart: resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" return resp + # File extensions that belong to the SPA or its assets. + # Anything else with an extension is not a valid app path and gets a hard 404. + _SPA_EXTENSIONS = { + "", ".html", ".js", ".css", ".ico", ".png", ".jpg", ".jpeg", + ".svg", ".webp", ".woff", ".woff2", ".ttf", ".otf", ".map", ".json", + } + @app.errorhandler(404) async def handle_404(error): # Return JSON 404 for API routes if request.path.startswith("/api/"): return jsonify({"error": "Not found"}), 404 - # Try to serve static file + + # Try to serve a real static file first path = request.path.lstrip("/") file_path = STATIC_DIR / path if path and file_path.is_file(): return await send_from_directory(STATIC_DIR, path) - # SPA fallback + + # Reject paths with file extensions the app doesn't serve. + # This turns scanner probes (.php, .asp, .cgi, etc.) into honest 404s + # instead of serving them the Vue SPA with a misleading 200. + from pathlib import PurePosixPath + suffix = PurePosixPath(request.path).suffix.lower() + if suffix not in _SPA_EXTENSIONS: + return "", 404 + + # SPA fallback for clean client-side routes (/notes/123, /chat, etc.) resp = await make_response( await send_from_directory(STATIC_DIR, "index.html") )