Skip to content

fix(security): consolidate HTTP 500 info-disclosure hardening - #818

Closed
groupthinking wants to merge 5 commits into
mainfrom
claude/determined-maxwell-udy1ne
Closed

fix(security): consolidate HTTP 500 info-disclosure hardening#818
groupthinking wants to merge 5 commits into
mainfrom
claude/determined-maxwell-udy1ne

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Summary

Sanitizes all HTTP 500 responses across the backend so internal exception details, dict/variable contents, and persisted error records are never leaked to clients. This is the consolidated version of the 500 info-disclosure work and supersedes the earlier one-off attempts.

Changes:

  • backend/main.py — close global exception-handler detail leak; stop the 400→500 swallow that surfaced internal messages.
  • backend/cloud_ai_routes.py, backend/cloud_api_endpoints.py, backend/real_api_endpoints.py — replace dynamic detail= payloads with static, safe messages; log the real error server-side only.
  • tests/unit/test_500_info_disclosure.py — new guard suite (4 tests) that scans handlers/responses for dynamic-detail leaks and includes synthetic-leak detectors to keep the guard honest.

Verification

pytest tests/unit/test_500_info_disclosure.py -o addopts=""
# 4 passed

All four changed source files compile; no conflict markers introduced.

Relationship to other open PRs

A cluster of overlapping automated PRs targets this same concern: #801, #804, #807, #810, #814, #815, #816. This branch consolidates that work into one reviewable change and is intended to supersede them. Recommend closing the redundant PRs in favour of this one once reviewed (do not merge the cluster in parallel — they would conflict on the same handlers).

Opened as draft for human review — not auto-merged.

🤖 Generated with Claude Code

https://claude.ai/code/session_014KW6wTcE4r8Uk11QpXBhVq


Generated by Claude Code

claude and others added 4 commits July 17, 2026 01:52
…sure (supersedes #801)

Multiple FastAPI handlers returned internal exception text to clients via
`HTTPException(status_code=500, detail=str(e))` (or f-strings embedding `{e}`),
leaking stack-adjacent messages, backend API errors, and database/Looker errors.
This is an information-disclosure vector (CWE-209).

#801 sanitized ~24 handlers but left three live endpoints leaking:
`generate_video_pack` and `generate_blueprint` (v1/router.py) and the mounted
`reporting_routes.py` dashboard endpoint — the exact gaps Copilot flagged on that
PR. A tree-wide scan surfaced 13 further leaks in cloud_ai_routes.py,
cloud_api_endpoints.py, and real_api_endpoints.py that #801 never touched.

Changes:
- Replace the dynamic `detail` in every 500 response across the backend with a
  static "Internal server error"; the full exception is now logged server-side
  (`logger.error(..., exc_info=True)`) so diagnostics are preserved.
- Add reporting_routes.py a module logger (previously none).
- Add tests/unit/test_500_info_disclosure.py: a hermetic source-scan guard that
  fails if any backend 500 response uses a dynamic `detail`, closing the
  test-coverage gap (existing exception-path tests asserted only status code, so
  they passed while the body leaked). Includes a self-check that the scanner
  detects a synthetic leak and ignores 4xx responses.

4xx responses (which echo client-supplied validation input) are intentionally
left unchanged. Verified: all touched files compile; ruff findings are identical
to the base branch (lint-neutral); guard test passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MF2xHBKyQBQtpdXt6bVRx
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).
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, Comment, Open in v0 Jul 17, 2026 2:45am

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 53fa3fc.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • [‘architecture-gap’, ‘bug’, ‘ci-cd’, ‘ci/cd’, ‘copilot-rabbit’, ‘documentation’, ‘duplicate’, ‘enhancement’, ‘frontend’, ‘github_actions’, ‘good first issue’, ‘help wanted’, ‘high-priority’, ‘invalid’, ‘javascript’, ‘ml-model’, ‘needs-triage’, ‘pipeline-critical’, ‘placeholder-code’, ‘priority:high’, ‘python’, ‘python:uv’, ‘question’, ‘styling’, ‘tests’, ‘v0’]

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f6e3580-f83b-4fe7-af9e-cfc11be66da8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/determined-maxwell-udy1ne
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/determined-maxwell-udy1ne

Comment @coderabbitai help to get the list of available commands.

Comment thread src/youtube_extension/backend/cloud_ai_routes.py
…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
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026
@linear-code
linear-code Bot marked this pull request as ready for review July 17, 2026 02:45
@groupthinking
groupthinking dismissed coderabbitai[bot]’s stale review July 17, 2026 04:18

The merge-base changed after approval.

groupthinking pushed a commit that referenced this pull request Jul 17, 2026
reporting_routes.generate_dashboard_url already returns a static
detail="Internal server error" (CWE-209 hardening), but
test_generate_dashboard_url_service_error still asserted the old
"Failed to generate" substring, leaving main CI red on the `test`
job (1 failed, 7163 passed).

Update the assertion to expect the sanitized body and to prove the
caught exception text ("Looker unavailable") does not leak. This is
the same test change already staged in PR #818; landing it directly
unblocks main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BmkwKENnbsy3v94m1sBid

Copy link
Copy Markdown
Owner Author

Closing as superseded by main (verified against main's code, not inferred). main's global exception handler + per-route 500 raise-paths are already sanitized (static Internal server error body; exception logged with exc_info=True) and are a superset of this branch's 500 hardening — merging would conflict with / partly revert main. The one CWE-209 sink still live on main is the non-500 return {"error": str(e)} dict-body leak, tracked in #831 (not superseded). Reopen if a specific hunk here is missing from main. — automated PR-remediation run


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants