mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-19 14:51:31 +02:00
A stored secret that failed to decrypt was used as if it were plaintext. unseal_headers handed the Fernet token back, so every run whose context carried auth sent `Authorization: gAAAAAB...` to the target and reported success. EncryptedJSON returned None, so a scan context's auth silently vanished and the run went out unauthenticated. load_proxy_endpoints returned an empty list, so a scan configured to go through a proxy went direct instead. Rotating SECRET_KEY was enough to trigger all three. decrypt_stored() now separates the two cases a lenient decrypt conflated. A value that carries no Fernet token was written before sealing shipped (2026-09-06) and is returned as it is. A value that carries one and will not open raises SecretDecryptionError, naming the header, the column or the proxy and what to do about it. A stage turns that into a failed activity with the message on it; the api answers 503 through a handler beside the existing DBAPIError one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X5roHYDYzSPRNaTkn8hSkJ
24 lines
663 B
Python
24 lines
663 B
Python
"""Map a stored secret this instance cannot open to a 503."""
|
|
|
|
from fastapi import Request
|
|
from fastapi.responses import JSONResponse
|
|
from starlette.status import HTTP_503_SERVICE_UNAVAILABLE
|
|
|
|
from app.core.crypto import SecretDecryptionError
|
|
from shared.logging import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
async def secret_decryption_handler(
|
|
request: Request, exc: SecretDecryptionError
|
|
) -> JSONResponse:
|
|
logger.error(
|
|
"stored secret could not be decrypted",
|
|
path=request.url.path,
|
|
method=request.method,
|
|
)
|
|
return JSONResponse(
|
|
status_code=HTTP_503_SERVICE_UNAVAILABLE, content={"detail": str(exc)}
|
|
)
|