Files
rengine/api/app/core/sanitize.py
T
Yogesh OjhaandClaude Opus 5 5908353df7 fix(api): eighteen endpoints answered a malformed query with a 500
A sweep of every GET operation with hostile query values found eighteen
that reached the database and failed there. Three causes, all of them
user input arriving unbounded.

A NUL byte in any text filter — project_slug, a WHOIS registrant, a
screenshot path — is rejected by Postgres while binding, so the request
died as a server error. NUL is never meaningful in a URL and is now
refused once, at the edge, with a sentence saying so.

Paging was unbounded upwards: a page number of 10^28 multiplied out to
an offset the database refuses, and the user list took any skip or
limit at all. Every paginated endpoint now shares one page shape with a
ceiling, and the user list bounds both of its own.

The activity filters typed their project, target and scan ids as plain
strings, so anything that was not a UUID reached the query; the RIPEstat
routes passed an ASN to int() without checking it was one. Both are now
refused as malformed input rather than answered with a crash.

The same sweep now reports no 5xx across 2,686 requests, and every list
endpoint still reads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6LcH8uUddjJbKrahf34ys
2026-09-07 05:47:50 +05:30

22 lines
852 B
Python

"""Reject request input the database cannot store before it reaches a query."""
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.status import HTTP_400_BAD_REQUEST
# Postgres rejects NUL in text, so a parameter carrying one fails while binding and
# surfaces as a 500. It is never meaningful in a URL.
_NUL = "\x00"
class RejectNulMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
query = request.url.query
if query and (_NUL in query or "%00" in query.lower()):
return JSONResponse(
status_code=HTTP_400_BAD_REQUEST,
content={"detail": "Query parameters must not contain NUL bytes."},
)
return await call_next(request)