Skip to content

Commit b7aa05f

Browse files
committed
Add path allowlist middleware to block vulnerability scanners
Only /get_term_info, /run_query, /health, /status are served. All other paths get a bare 404 that reveals nothing about the stack. Scanner probe count exposed in /status endpoint.
1 parent 144dbe8 commit b7aa05f

1 file changed

Lines changed: 27 additions & 1 deletion

File tree

src/vfbquery/ha_api.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,31 @@ def snapshot(self):
174174
"total_served": self._total_served,
175175
}
176176

177+
# ---------------------------------------------------------------------------
178+
# Security — path allowlist
179+
# Only these paths are served; everything else gets an empty 404 so that
180+
# vulnerability scanners learn nothing about the stack.
181+
# ---------------------------------------------------------------------------
182+
183+
ALLOWED_PATHS = frozenset({"/get_term_info", "/run_query", "/health", "/status"})
184+
185+
186+
@web.middleware
187+
async def security_middleware(request, handler):
188+
"""Reject requests to unknown paths with a minimal 404."""
189+
if request.path not in ALLOWED_PATHS:
190+
request.app["_scanner_probes"] = request.app.get("_scanner_probes", 0) + 1
191+
# Log first occurrence per path, then every 100th to avoid flooding
192+
count = request.app["_scanner_probes"]
193+
if count <= 10 or count % 100 == 0:
194+
log.warning(
195+
"Blocked probe #%d: %s %s from %s",
196+
count, request.method, request.path, request.remote,
197+
)
198+
return web.Response(status=404)
199+
return await handler(request)
200+
201+
177202
# ---------------------------------------------------------------------------
178203
# Query-type → VFBquery function mapping
179204
#
@@ -515,6 +540,7 @@ async def handle_status(request):
515540
"cache_hits": rcache.hits,
516541
"coalesced_total": coalescer.coalesced_total,
517542
"coalesced_in_flight": coalescer.in_flight_count,
543+
"scanner_probes_blocked": request.app.get("_scanner_probes", 0),
518544
})
519545

520546

@@ -543,7 +569,7 @@ def create_app(max_workers=None, max_concurrent=None, max_queue_depth=None,
543569
if cache_ttl is None:
544570
cache_ttl = int(os.getenv("VFBQUERY_CACHE_TTL", "300"))
545571

546-
app = web.Application()
572+
app = web.Application(middlewares=[security_middleware])
547573

548574
# Routes
549575
app.router.add_get("/get_term_info", handle_get_term_info)

0 commit comments

Comments
 (0)