Files
rengine/api/app/core/secret_errors.py
T
Yogesh OjhaandClaude Opus 5 d8122ae9c1 fix(secrets): refuse to run on a credential this instance cannot open
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
2026-09-13 11:14:40 +05:30

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)}
)