fix(security): eliminate exception-detail leakage from 500 responses (CWE-209) - #883
fix(security): eliminate exception-detail leakage from 500 responses (CWE-209)#883groupthinking wants to merge 3 commits into
Conversation
The source-scan guard added for the 500 info-disclosure work only matched
inline detail=str(e) / detail=f"...{e}...". It silently passed while three
handlers still leaked internal state through detail shapes it did not model:
- cloud_api_endpoints.py process_video_cloud -> detail={... "message": error_msg ...}
- cloud_api_endpoints.py process_video_task -> detail=error_msg (bare variable)
- real_api_endpoints.py process_video_real_api -> detail={... "message": error_msg ...}
Fix all three to a static "Internal server error" (the exception is logged
server-side via logger.error(..., exc_info=True)), and rewrite the guard to
flag any 500 detail= that is not an inline static string literal — a leading
'{' (dict) or identifier char (f-string, str(), or a bare variable) now fails
the scan. Self-checks extended to cover the dict and variable shapes.
test_error_response_includes_video_url asserted the old leaking body; it is
replaced by test_error_response_is_sanitized, which asserts the 500 body is
exactly "Internal server error" and contains neither the exception nor the
caller-supplied video_url.
Strict superset of the #807 approach; the router.py/reporting_routes.py sinks
#804 targeted are already clean on main and covered by the tree-wide guard.
…leak, 400→500 swallow Round of fixes from the CodeRabbit full review and the Vercel VADE bot on #815: - main.py global_exception_handler leaked str(exc) and the exception class name (error_type) in its 500 JSONResponse body (VADE finding). Return a static body; the full exception is already logged with exc_info=True. - cloud_api_endpoints.process_video_task_handler persisted error_msg (containing str(e)) as the Firestore 'error_message', which get_video_status / get_video_result return verbatim to clients — exfiltrating the exception despite the sanitized 500. Persist a generic message; keep the detail in logs only. - real_api_endpoints batch_process_videos and search_youtube_videos caught their own HTTPException(400) validation errors in the broad 'except Exception' and turned them into 500s. Re-raise HTTPException first to preserve the 400. - cloud_ai_routes: add exc_info=True to the five 500-handler logger.error calls so the traceback is retained server-side. - Guard: exception handlers are a second 500 sink the HTTPException scan didn't model. Add test_no_disclosure_in_500_exception_handlers — it flags any @app.exception_handler that returns 500 while placing str(exc)/str(e) or __class__.__name__ in the response body (docstrings/comments/log lines excluded).
…ler test - analyze_video_multi_provider caught HTTPException(400) from parse_analysis_types in its broad 'except Exception' and re-raised it as a generic 500, masking a client error (flagged in review). Add 'except HTTPException: raise' to mirror analyze_video / analyze_batch_videos, plus a regression test asserting an invalid analysis type returns 400. - Update the stale global-exception-handler test: it asserted the 500 body leaks 'error_type' (the exception class name), which the info-disclosure hardening deliberately removes (CWE-209). Assert the secure contract instead: neither the exception type nor its message appears in the response. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014KW6wTcE4r8Uk11QpXBhVq
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Agent Completion Truth Gate: BLOCKEDReasons: Machine-readable verdict{
"details": {
"invalid_fields": [
"issue.number",
"policy.agent_login",
"policy.run_id"
]
},
"reasons": [
"invalid_payload"
],
"verdict": "blocked"
} |
|
Closing as redundant / superseded — no action needed. This PR's head commit (
This branch is ~150 commits behind Generated by Claude Code |
Summary
Hardens the backend so that HTTP 500 responses never disclose internal exception
detail to callers (CWE-209 information disclosure), while preserving full
diagnostics server-side and keeping intentional 4xx client errors intact.
Changes
Static 500 bodies (no
str(exc)/ class name to the client):backend/main.py— globalExceptionhandler no longer returnsstr(exc)indetailnor anerror_typederived fromexc.__class__.__name__; the fullexception (type, message, traceback) is logged via
exc_info=Trueonly.backend/cloud_api_endpoints.py—process_video_cloudandprocess_video_task_handlerreturn a static"Internal server error"detailinstead of a dict echoing the exception message,
video_url, and timestamp.The persisted
error_messagewritten to Firestore state (surfaced to clients bystatus/result endpoints) is also made generic.
backend/real_api_endpoints.py—process_video500 path returns a staticstring instead of a detail dict carrying the exception message and
video_url.Preserve intentional client errors (stop 4xx → 500 swallowing):
cloud_ai_routes.py(analyze_video_multi_provider) andreal_api_endpoints.py(batch_process_videos,search_youtube_videos) nowre-raise
HTTPExceptionbefore the broadexcept Exception, so a deliberate400 (e.g. bad
analysis_types) is no longer converted into a generic 500.Diagnostics preserved: every touched handler logs with
exc_info=Trueso thefull traceback remains available in server logs.
Tests
tests/unit/test_500_info_disclosure.py— expanded AST-based guard that fails ifany handler puts dynamic exception detail into a 500 body; includes synthetic
positive-control cases proving the guard actually detects leaks. Passing locally.
test_backend_main.py,test_cloud_routes.py,test_real_api_endpoints.pyto assert the new static-body contract.
Verification
test_500_info_disclosure.py: 4 passed locally.py_compile).merge-base, and all present rule classes are already in CI's ignore list).
🤖 Generated with Claude Code
Generated by Claude Code