Skip to content

fix(security): sanitize HTTP 500 responses to prevent info disclosure - #814

Closed
groupthinking wants to merge 11 commits into
mainfrom
claude/determined-maxwell-dczolv
Closed

fix(security): sanitize HTTP 500 responses to prevent info disclosure#814
groupthinking wants to merge 11 commits into
mainfrom
claude/determined-maxwell-dczolv

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Summary

Closes remaining HTTP 500 information-disclosure leaks (CWE-209) across the cloud and real API routers. Exception messages, stack context, and request-derived values (video_url, timestamps) were being returned to clients inside the HTTPException detail field. This replaces every such dynamic detail with a static "Internal server error" string while preserving — and in several cases adding — server-side logger.error(..., exc_info=True) so operators keep full diagnostics.

Changes

  • cloud_ai_routes.py — 5 handlers: dynamic detail=f"...{str(e)}" → static "Internal server error".
  • cloud_api_endpoints.py — replaced dict-shaped 500 details (leaking message/video_url/timestamp) with a static string; added logger.error(..., exc_info=True) where missing.
  • real_api_endpoints.py — same treatment across 6 handlers, including the dict-shaped leak in the v2 process endpoint.
  • tests/unit/test_500_info_disclosure.pynew static-guard test that scans the router source for dynamic 500 details and fails on any leak (plus a synthetic-leak positive control).
  • tests/unit/test_cloud_routes.py — updated one assertion to match the sanitized response body.

Verification

  • pytest tests/unit/test_500_info_disclosure.py → 2 passed (guard + synthetic-leak control).
  • test_cloud_routes.py assertion updated to detail == "Internal server error".

Notes

Supersedes the earlier #801 approach and overlaps thematically with the other open 500-hardening PRs (#804, #807, #810). Left as a draft pending human review and de-duplication against those PRs before merge.

🤖 Generated with Claude Code


Generated by Claude Code

claude and others added 3 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
…s in cloud/real routers

Follow-up on the merge resolution (4c9b205): three 500 handlers still returned
internal exception text, flagged in the Copilot review but not yet fixed:

- cloud_api_endpoints.py: process_video_cloud returned a dict detail whose
  "message" embedded f"Cloud processing failed: {str(e)}"; process_video_task_handler
  returned detail=error_msg (same interpolation).
- real_api_endpoints.py: process_video_real_api returned a dict detail embedding
  f"Real API processing failed: {str(e)}".

All three now return a static detail="Internal server error". error_msg is still
built and logged server-side via logger.error, so diagnostics are preserved; only
the client-facing body is sanitized. No test asserted the leaked dict/message.

Remaining, still-open items (already noted by the Copilot review, deferred as
separate semantics-touching changes): the JSONResponse global_exception_handler in
backend/main.py, the 503/429 handlers in cloud_ai_routes.py (exception ordering),
positional HTTPException(500, str(e)) sites in api/advanced_video_routes.py, and
upgrading the regression guard to AST so it covers those forms.

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

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 657f7dea-74ed-401e-b893-2a02b3ae02ac

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
📝 Walkthrough

Walkthrough

Updated FastAPI exception handlers across cloud AI, cloud API, and real API routes to return generic 500 responses while retaining server-side exception logging.

Changes

API Error Handling

Layer / File(s) Summary
Cloud AI route error responses
src/youtube_extension/backend/cloud_ai_routes.py
Provider status, video analysis, batch analysis, and multi-provider failures now return detail="Internal server error" instead of exception-derived details.
Cloud API endpoint error responses
src/youtube_extension/backend/cloud_api_endpoints.py
Processing, task, batch, status, and result failures now use static 500 details; selected handlers log stack traces with exc_info=True.
Real API endpoint error responses
src/youtube_extension/backend/real_api_endpoints.py
Real API failures no longer return exception text or structured exception payloads and instead use a generic 500 response.
Estimated code review effort: 2 (Simple) ~10 minutes

Suggested labels: security

Suggested reviewers: claude

Poem

Exceptions hide behind a quiet door,
Logs still carry what they bore.
Cloud routes speak one guarded phrase,
Real APIs follow matching ways.
Safer errors light the server’s maze.

🚥 Pre-merge checks | ✅ 5 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Enforce Copilot Verification ⚠️ Warning No explicit GitHub Copilot APPROVED review is present; only human/CodeRabbit notes and a generic pass marker exist. Obtain a GitHub Copilot review on the PR with state APPROVED and record it; human approvals cannot satisfy this gate.
Require Ai Unit Tests ⚠️ Warning PR #814 has tests committed, but its labels are high-priority/python/security/tests; copilot-rabbit is missing. Add the copilot-rabbit label before merge; the AI-generated regression test is already committed alongside the code.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main security change: sanitizing HTTP 500 responses to avoid information disclosure.
Description check ✅ Passed The description matches the PR changes and accurately describes the error-response hardening and regression test update.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/determined-maxwell-dczolv
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/determined-maxwell-dczolv

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

@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 42e9ff9.
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

@groupthinking groupthinking added high-priority Urgent - blocks revenue or core functionality tests labels Jul 17, 2026 — with Claude

Copy link
Copy Markdown
Owner Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@groupthinking: Reviewing pull request #814 with focus on production-impacting security regressions and information-disclosure paths.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Owner Author

Closing as a duplicate of #807.

This PR's head (f25030c) is byte-identical to #807 (same commit SHA, same 5-file / +122 −33 diff — the HTTP 500 info-disclosure sanitization across cloud_ai_routes.py, cloud_api_endpoints.py, real_api_endpoints.py plus the test_500_info_disclosure.py guard).

#807 is the canonical PR for this work: it's non-draft, older, carries the full investigation writeup (supersedes #801), and already has review history. Consolidating there so there aren't two competing PRs targeting protected main. Reopen if #807 is abandoned and this branch should carry the work instead.


Generated by Claude Code

…itization)

The existing test_error_response_includes_video_url asserted that the
/api/v2/process-video 500 body echoed the request video_url — the exact
CWE-209 information-disclosure behaviour the sanitization removes. It failed
in CI (assert 'auJzb1D-fag' in 'Internal server error') because the handler
now returns a static detail.

Invert it into test_error_response_does_not_leak_internal_state: assert the
detail is exactly 'Internal server error' and contains neither the
video_url nor the exception text.

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

Copy link
Copy Markdown
Owner Author

Correction — not closing this after all; it now supersedes #807.

After my earlier "duplicate of #807" note, CI on the shared commit f25030c surfaced a real regression in the test job:

FAILED tests/unit/test_real_api_endpoints.py::TestProcessVideoEndpoint::test_error_response_includes_video_url
  AssertionError: assert 'auJzb1D-fag' in 'Internal server error'

That pre-existing test asserted the 500 body echoes the request video_url — precisely the CWE-209 leak this PR removes. It fails identically on #807 (same commit). I've fixed it here in 321d963 by inverting the test into test_error_response_does_not_leak_internal_state (asserts detail == "Internal server error" and that neither the video_url nor the exception text appears). Verified locally: TestProcessVideoEndpoint → 8 passed; test_500_info_disclosure.py guard → 2 passed.

So this branch = #807's diff + the test-regression fix #807 is missing. Recommend landing #814 and closing #807 as the stale one.

Unrelated: the trivy check fails with a config error (unable to find image "eventrelay:test"JOB_STATUS_CONFIGURATION_ERROR) — the scan targets a Docker image no prior step builds/tags. Pre-existing infra issue, fails on main too; out of scope for this PR and needs a separate .github/workflows/security.yml fix.

Still targeting protected mainnot auto-merged; human sign-off required.


Generated by Claude Code

@groupthinking
groupthinking marked this pull request as ready for review July 17, 2026 02:19
Copilot AI review requested due to automatic review settings July 17, 2026 02:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/youtube_extension/backend/cloud_api_endpoints.py (1)

221-226: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not persist exception text as client-visible task state.

Although the immediate 500 response is generic, error_msg still contains str(e) and is written to Firestore at Line 220. get_video_status and get_video_result return state.error_message, allowing clients to recover the exception through polling. Store a fixed user-safe message and log the original exception with exc_info=True; the new “logged above only” comment is currently inaccurate.

As per coding guidelines, outputs must be sanitized for security.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/youtube_extension/backend/cloud_api_endpoints.py` around lines 221 - 226,
Update the error-state persistence in the surrounding endpoint handler to store
a fixed user-safe message instead of exception-derived error_msg text. Keep the
original exception available only in the logger call, adding exc_info=True for
traceback details, and revise the nearby comment to accurately describe this
behavior; ensure get_video_status and get_video_result cannot expose str(e)
through state.error_message.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/youtube_extension/backend/cloud_ai_routes.py`:
- Around line 230-232: Update the exception handlers in
src/youtube_extension/backend/cloud_ai_routes.py at lines 230-232, 264-271,
301-304, and 326-328, src/youtube_extension/backend/cloud_api_endpoints.py at
lines 144-149, and src/youtube_extension/backend/real_api_endpoints.py at lines
120-125 to preserve original tracebacks while raising sanitized HTTPException
responses. Change each logger.error call to include exc_info=True or use
logger.exception(...), while retaining the existing contextual messages and
response behavior.

In `@src/youtube_extension/backend/real_api_endpoints.py`:
- Around line 173-177: Preserve explicit HTTPException responses by adding an
HTTPException-specific re-raise before the broad Exception handler in both
batch_process_videos at src/youtube_extension/backend/real_api_endpoints.py
lines 173-177 and the search-result validation flow at lines 431-435; leave
other exceptions handled by the existing logging and 500 response path.

---

Outside diff comments:
In `@src/youtube_extension/backend/cloud_api_endpoints.py`:
- Around line 221-226: Update the error-state persistence in the surrounding
endpoint handler to store a fixed user-safe message instead of exception-derived
error_msg text. Keep the original exception available only in the logger call,
adding exc_info=True for traceback details, and revise the nearby comment to
accurately describe this behavior; ensure get_video_status and get_video_result
cannot expose str(e) through state.error_message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1ab9536-b6da-4be3-a9b8-69d7ebc01c45

📥 Commits

Reviewing files that changed from the base of the PR and between 6aa90db and f25030c.

⛔ Files ignored due to path filters (2)
  • tests/unit/test_500_info_disclosure.py is excluded by !tests/**
  • tests/unit/test_cloud_routes.py is excluded by !tests/**
📒 Files selected for processing (3)
  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
  • src/youtube_extension/backend/real_api_endpoints.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • groupthinking/uvai-skills (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test
⚠️ CI failures not shown inline (10)

GitHub Actions: Security Scan / trivy: fix(security): sanitize HTTP 500 responses to prevent info disclosure

Conclusion: failure

View job details

 `#9` 29.57 Setting up libgbm1:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.57 Setting up libgl1-mesa-dri:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.58 Setting up gcc-14 (14.2.0-19) ...
 `#9` 29.58 Setting up librsvg2-2:amd64 (2.60.0+dfsg-1) ...
 `#9` 29.59 Setting up libpocketsphinx3:amd64 (0.8+5prealpha+1-15+b4) ...
 `#9` 29.59 Setting up libavcodec61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.59 Setting up g++-14-x86-64-linux-gnu (14.2.0-19) ...
 `#9` 29.59 Setting up g++-x86-64-linux-gnu (4:14.2.0-1) ...
 `#9` 29.60 Setting up curl (8.14.1-2+deb13u4) ...
 `#9` 29.60 Setting up g++-14 (14.2.0-19) ...
 `#9` 29.60 Setting up libsdl2-2.0-0:amd64 (2.32.4+dfsg-1) ...
 `#9` 29.60 Setting up libglx-mesa0:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.61 Setting up libglx0:amd64 (1.7.0-1+b2) ...
 `#9` 29.61 Setting up libavformat61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.61 Setting up gcc (4:14.2.0-1) ...
 `#9` 29.62 Setting up libgl1:amd64 (1.7.0-1+b2) ...
 `#9` 29.63 Setting up libavfilter10:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.63 Setting up g++ (4:14.2.0-1) ...
 `#9` 29.64 update-alternatives: using /usr/bin/g++ to provide /usr/bin/c++ (c++) in auto mode
 `#9` 29.64 Setting up build-essential (12.12) ...
 `#9` 29.64 Setting up libavdevice61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.64 Setting up ffmpeg (7:7.1.5-0+deb13u1) ...
 `#9` 29.65 Processing triggers for libc-bin (2.41-12+deb13u3) ...
 `#9` 29.79 �[38;5;79m - Installing pre-requisites�[0m
 `#9` 29.79
 `#9` 29.79 WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
 `#9` 29.79
 `#9` 29.84 Hit:1 http://deb.debian.org/debian trixie InRelease
 `#9` 29.84 Hit:2 http://deb.debian.org/debian trixie-updates InRelease
 `#9` 29.84 Hit:3 http://deb.debian.org/debian-security trixie-security InRelease
 `#9` 29.87 Reading package lists...
 `#9` 30.44 Building dependency tree...
 `#9` 30.59 Reading state information...
 `#9` 30.61 All packages are up to date.
 `#9` 30.61
 `#9` 30.61 WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
 `#9` 30.61
 `#9` 30.62 Reading pac...

GitHub Actions: Security Scan / trivy: fix(security): sanitize HTTP 500 responses to prevent info disclosure

Conclusion: failure

View job details

##[group]Run entrypoint.sh
 �[36;1mentrypoint.sh�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CODEQL_ACTION_FEATURE_MULTI_LANGUAGE: false
   CODEQL_ACTION_FEATURE_SANDWICH: false
   CODEQL_ACTION_FEATURE_SARIF_COMBINE: true
   CODEQL_ACTION_FEATURE_WILL_UPLOAD: true
   CODEQL_ACTION_VERSION: 4.37.1
   CODEQL_ACTION_ANALYSIS_KEY: .github/workflows/security.yml:trivy
   CODEQL_WORKFLOW_STARTED_AT:
   CODEQL_ACTION_JOB_STATUS: JOB_STATUS_CONFIGURATION_ERROR
   INPUT_SCAN_TYPE: image
   INPUT_IMAGE_REF: eventrelay:test
   INPUT_SCAN_REF: .
   INPUT_TRIVYIGNORES: .trivyignore
   INPUT_GITHUB_PAT:
   INPUT_LIMIT_SEVERITIES_FOR_SARIF:
   TRIVY_CACHE_DIR: /home/runner/work/EventRelay/EventRelay/.cache/trivy
 ##[endgroup]
 Found ignorefile '.trivyignore':
 # Trivy Ignore File
 # This file contains vulnerabilities that are accepted risks or false positives
 # Format: CVE-ID or vulnerability ID, one per line
 # Comments start with #
 # Go crypto certificate validation issues in base images
 # These are typically fixed by updating the base image in future releases
 # and are not directly actionable in application code
 CVE-2025-58183
 CVE-2025-61729
 # Add other CVEs here as needed with justification comments
 Running Trivy with options: trivy image eventrelay:test
 	INFO	[vuln] Vulnerability scanning is enabled
 	INFO	[secret] Secret scanning is enabled
 	INFO	[secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
 	INFO	[secret] Please see https://trivy.dev/docs/v0.70/guide/scanner/secret#recommendation for faster secret detection
 📣 �[34mNotices:�[0m
   - Version 0.72.0 of Trivy is now available, current version is 0.70.0
 To suppress version checks, run Trivy scans with the --skip-version-check flag
 	FATAL	Fatal error	run error: image scan error: scan error: unable to initialize a scan service: unable to initialize artifact: unable to initialize container image: unable to find the specified image "eventrel...

GitHub Actions: Security Scan / trivy: fix(security): sanitize HTTP 500 responses to prevent info disclosure

Conclusion: failure

View job details

##[group]Run github/codeql-action/upload-sarif@v4
 with:
   sarif_file: trivy-results.sarif
   checkout_path: /home/runner/work/EventRelay/EventRelay
   ***REDACTED***
   matrix: null
   wait-for-processing: true
 ##[endgroup]
 ##[error]Path does not exist: trivy-results.sarif

GitHub Actions: Security Scan / 0_trivy.txt: fix(security): sanitize HTTP 500 responses to prevent info disclosure

Conclusion: failure

View job details

 `#9` 29.57 Setting up libgbm1:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.57 Setting up libgl1-mesa-dri:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.58 Setting up gcc-14 (14.2.0-19) ...
 `#9` 29.58 Setting up librsvg2-2:amd64 (2.60.0+dfsg-1) ...
 `#9` 29.59 Setting up libpocketsphinx3:amd64 (0.8+5prealpha+1-15+b4) ...
 `#9` 29.59 Setting up libavcodec61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.59 Setting up g++-14-x86-64-linux-gnu (14.2.0-19) ...
 `#9` 29.59 Setting up g++-x86-64-linux-gnu (4:14.2.0-1) ...
 `#9` 29.60 Setting up curl (8.14.1-2+deb13u4) ...
 `#9` 29.60 Setting up g++-14 (14.2.0-19) ...
 `#9` 29.60 Setting up libsdl2-2.0-0:amd64 (2.32.4+dfsg-1) ...
 `#9` 29.60 Setting up libglx-mesa0:amd64 (25.0.7-2+deb13u1) ...
 `#9` 29.61 Setting up libglx0:amd64 (1.7.0-1+b2) ...
 `#9` 29.61 Setting up libavformat61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.61 Setting up gcc (4:14.2.0-1) ...
 `#9` 29.62 Setting up libgl1:amd64 (1.7.0-1+b2) ...
 `#9` 29.63 Setting up libavfilter10:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.63 Setting up g++ (4:14.2.0-1) ...
 `#9` 29.64 update-alternatives: using /usr/bin/g++ to provide /usr/bin/c++ (c++) in auto mode
 `#9` 29.64 Setting up build-essential (12.12) ...
 `#9` 29.64 Setting up libavdevice61:amd64 (7:7.1.5-0+deb13u1) ...
 `#9` 29.64 Setting up ffmpeg (7:7.1.5-0+deb13u1) ...
 `#9` 29.65 Processing triggers for libc-bin (2.41-12+deb13u3) ...
 `#9` 29.79 �[38;5;79m - Installing pre-requisites�[0m
 `#9` 29.79
 `#9` 29.79 WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
 `#9` 29.79
 `#9` 29.84 Hit:1 http://deb.debian.org/debian trixie InRelease
 `#9` 29.84 Hit:2 http://deb.debian.org/debian trixie-updates InRelease
 `#9` 29.84 Hit:3 http://deb.debian.org/debian-security trixie-security InRelease
 `#9` 29.87 Reading package lists...
 `#9` 30.44 Building dependency tree...
 `#9` 30.59 Reading state information...
 `#9` 30.61 All packages are up to date.
 `#9` 30.61
 `#9` 30.61 WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
 `#9` 30.61
 `#9` 30.62 Reading pac...

GitHub Actions: CI / lint-python: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

_video_routes.py:8:1
    |
  7 | import logging
  8 | from typing import Dict, List, Optional, Tuple
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  9 |
 10 | from fastapi import APIRouter, HTTPException
    |
 UP006 Use `list` instead of `List` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:33:27
    |
 31 |     """Request for extracting timestamped events."""
 32 |     video_url: str
 33 |     event_types: Optional[List[str]] = Field(
    |                           ^^^^
 34 |         None,
 35 |         description="Event types to focus on (e.g., ['code_change', 'api_call'])"
    |
 help: Replace with `list`
 UP006 Use `list` instead of `List` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:65:15
    |
 63 |     """Request for comparing multiple segments."""
 64 |     video_url: str
 65 |     segments: List[Tuple[str, str]] = Field(
    |               ^^^^
 66 |         ...,
 67 |         description="List of (start_time, end_time) tuples to compare"
    |
 help: Replace with `list`
 UP006 Use `tuple` instead of `Tuple` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:65:20
    |
 63 |     """Request for comparing multiple segments."""
 64 |     video_url: str
 65 |     segments: List[Tuple[str, str]] = Field(
    |                    ^^^^^
 66 |         ...,
 67 |         description="List of (start_time, end_time) tuples to compare"
    |
 help: Replace with `tuple`
 UP006 Use `dict` instead of `Dict` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:84:13
    |
 82 |     video_url: str
 83 |     prompt: str
 84 |     schema: Dict = Field(
    |             ^^^^
 85 |         ...,
 86 |         description="JSON schema for structured output",
    |
 help: Replace with `dict`
 W293 Blank line contains whitespace
    --> src/youtube_extension/backend/api/advanced_video_routes.py:111:1
     |
 109 |     ""...

GitHub Actions: CI / 3_guards.txt: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

##[group]Run # Opening/closing conflict sentinels always carry a label after the
 �[36;1m# Opening/closing conflict sentinels always carry a label after the�[0m
 �[36;1m# space, so this never matches decorative "=======" underlines.�[0m
 �[36;1mif git grep -nE '^(<<<<<<<|>>>>>>>) ' -- . ':(exclude).github/workflows/ci.yml'; then�[0m
 �[36;1m  echo "::error::Committed merge-conflict markers found (see matches above)."�[0m

GitHub Actions: CI / 4_lint-python.txt: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

_video_routes.py:8:1
    |
  7 | import logging
  8 | from typing import Dict, List, Optional, Tuple
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  9 |
 10 | from fastapi import APIRouter, HTTPException
    |
 UP006 Use `list` instead of `List` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:33:27
    |
 31 |     """Request for extracting timestamped events."""
 32 |     video_url: str
 33 |     event_types: Optional[List[str]] = Field(
    |                           ^^^^
 34 |         None,
 35 |         description="Event types to focus on (e.g., ['code_change', 'api_call'])"
    |
 help: Replace with `list`
 UP006 Use `list` instead of `List` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:65:15
    |
 63 |     """Request for comparing multiple segments."""
 64 |     video_url: str
 65 |     segments: List[Tuple[str, str]] = Field(
    |               ^^^^
 66 |         ...,
 67 |         description="List of (start_time, end_time) tuples to compare"
    |
 help: Replace with `list`
 UP006 Use `tuple` instead of `Tuple` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:65:20
    |
 63 |     """Request for comparing multiple segments."""
 64 |     video_url: str
 65 |     segments: List[Tuple[str, str]] = Field(
    |                    ^^^^^
 66 |         ...,
 67 |         description="List of (start_time, end_time) tuples to compare"
    |
 help: Replace with `tuple`
 UP006 Use `dict` instead of `Dict` for type annotation
   --> src/youtube_extension/backend/api/advanced_video_routes.py:84:13
    |
 82 |     video_url: str
 83 |     prompt: str
 84 |     schema: Dict = Field(
    |             ^^^^
 85 |         ...,
 86 |         description="JSON schema for structured output",
    |
 help: Replace with `dict`
 W293 Blank line contains whitespace
    --> src/youtube_extension/backend/api/advanced_video_routes.py:111:1
     |
 109 |     ""...

GitHub Actions: CI / guards: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

##[group]Run # Opening/closing conflict sentinels always carry a label after the
 �[36;1m# Opening/closing conflict sentinels always carry a label after the�[0m
 �[36;1m# space, so this never matches decorative "=======" underlines.�[0m
 �[36;1mif git grep -nE '^(<<<<<<<|>>>>>>>) ' -- . ':(exclude).github/workflows/ci.yml'; then�[0m
 �[36;1m  echo "::error::Committed merge-conflict markers found (see matches above)."�[0m

GitHub Actions: CI / test: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

est_video_processing_service.py::TestProcessVideoToSoftware::test_failed_video_analysis_raises
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
  [   ERROR] youtube_extension.backend.services.video_processing_service: Video-to-software processing failed: Video processing failed: Analysis failed
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_fallback_to_vercel_when_primary_url_missing
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_build_failed_when_no_urls
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_exception_propagated
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_pr...

GitHub Actions: CI / 2_test.txt: fix(security): sanitize all HTTP 500 responses to prevent info disclosure (supersedes #801)

Conclusion: failure

View job details

est_video_processing_service.py::TestProcessVideoToSoftware::test_failed_video_analysis_raises
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
  [   ERROR] youtube_extension.backend.services.video_processing_service: Video-to-software processing failed: Video processing failed: Analysis failed
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_fallback_to_vercel_when_primary_url_missing
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_build_failed_when_no_urls
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_processing_service: Processing video to software: https://www.youtube.com/watch?v=auJzb1D-fag
  [    INFO] youtube_extension.backend.services.video_processing_service: ✅ Video processor initialized successfully
 PASSED                                                                   [ 95%]
 tests/unit/test_video_processing_service.py::TestProcessVideoToSoftware::test_exception_propagated
 -------------------------------- live log call ---------------------------------
  [    INFO] youtube_extension.backend.services.video_pr...
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{py,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,js,jsx,ts,tsx}: Use Python 3.9+ and Node 18+ for development
Never hardcode API keys, database URLs, or secrets in code
Make minimal, surgical changes and avoid deleting working code unless fixing security issues

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.py: Always use type hints for Python functions
Use Black formatter with 88 character line length for Python code
Follow PEP 8 conventions for Python code
Use descriptive variable names and add docstrings to all public functions in Python
Group imports in Python: standard library, third-party, local
Use SQLAlchemy ORM and never write raw SQL queries
Use environment variables via os.getenv() or pydantic-settings to access configuration
Wrap database operations in try-except blocks and use context managers or FastAPI dependencies for connection cleanup
Implement comprehensive error handling with proper logging in all functions
Version APIs using /api/v1/ prefix for stability
Use JSON-RPC 2.0 protocol for all MCP communication
Follow the single-flow workflow: YouTube link → context extraction → agent dispatch → outputs
Use context managers for resource management in Python code
Implement comprehensive input validation and sanitize outputs for security
Use parameterized queries and SQLAlchemy ORM to prevent SQL injection
Define API request/response models using Pydantic for FastAPI endpoints
Store the single unified workflow as the only workflow; never introduce alternate flows or manual triggers
Use SQLAlchemy with connection pooling for database connections and manage sessions with context managers
Provide sensible defaults for non-sensitive configuration in Python settings
Maintain backward compatibility and do not break existing API endpoints
Include comprehensive logging for debugging in MCP implementations

**/*.py: Format Python code with Black using an 88-character line length.
Use Ruff with rules E, W, F, I, B, C4, and UP; E501 is ignored.
Use strict mypy checking with untyped function definitions disallowed.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py

⚙️ CodeRabbit configuration file

Python backend code. Check for type hints, proper exception handling, async context manager usage, and potential blocking calls in async functions. Flag any bare except clauses or missing timeout parameters on network calls. CRITICAL: Flag any file that contains placeholder/stub implementations — especially in unified_ai_sdk. Any class or function that says "TODO: Replace with production implementation" or returns mock/fake data must be flagged as a blocking issue. Flag any code generation output that reaches users without AST validation or syntax checking.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*.{py,js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain >80% code coverage for new features

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*.{py,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,ts,tsx}: Keep frontend and backend data models synchronized using matching Pydantic (backend) and TypeScript (frontend) interfaces
Use type-safe interfaces for backend-frontend data exchange

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*

📄 CodeRabbit inference engine (Custom checks)

**/*: Strictly verify that GitHub Copilot has explicitly reviewed and approved the pull request; human approvals alone must not satisfy this check.
Before allowing a merge, require the copilot-rabbit label and AI-generated unit tests committed alongside the code changes; fail the check if either is missing.

**/*: Follow the documented event naming convention <domain>.<entity>.<action>, such as youtube.video.captured.
Use the service-container dependency injection pattern for backend dependencies.
Never infer SDK types from tests or API documentation alone; use backend response models as the authority.
When auditing branches, use the branch-cleanup skill and its six-gate fail-test harness; archive branches with git tag archive/<branch> before deletion, and do not rely on three-dot diffs or git merge-tree for orphaned branches.

For Vercel-specific work, include https://vercel.com/docs/llms-full.txt in the AI assistant context set.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Format Python code with Black using an 88-character line length.
Sort Python imports with isort using the Black profile.
Use Ruff with E, W, F, I, B, C4, and UP rules; E501 is ignored.
Use mypy strict mode; untyped function definitions are disallowed.
Target Python 3.9 or newer.
Validate backend inputs with Pydantic and sanitize subprocess arguments.
Maintain strict mypy type safety in the Python backend.
Use the required Anthropic SDK parameters thinking={"type": "adaptive"} and output_config={"effort": "..."} with the current model string claude-opus-4-8; do not add TypeError compatibility fallbacks.

src/**/*.py: Do not introduce alternative workflows or manual triggers that bypass the single YouTube link → transcript → events → agents → outputs pipeline.
Use event names in the <domain>.<entity>.<action> format.
Use the service-container dependency-injection pattern for dependencies.
Use Pydantic input validation and sanitize subprocess arguments.
Production code must use real behavior only; do not add mock delays or fake data.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*.{py,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,js,jsx}: Do not use mock delays, fake data, or simulated responses in production code; production must remain REAL_MODE_ONLY.
Do not hard-code secrets, keys, or credentials; store them in .env files that are gitignored.

Do not include secrets or API keys in source code; load them from environment variables instead.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
**/*.{py,pyw}

📄 CodeRabbit inference engine (AGENTS.md)

Write Python code to remain compatible with Linux and Windows where possible, including correct handling of asyncio event loops.

Files:

  • src/youtube_extension/backend/cloud_ai_routes.py
  • src/youtube_extension/backend/real_api_endpoints.py
  • src/youtube_extension/backend/cloud_api_endpoints.py
🪛 GitHub Actions: CI / 4_lint-python.txt
src/youtube_extension/backend/cloud_api_endpoints.py

[error] 12-31: I001 (isort): Import block is un-sorted or un-formatted.


[error] 17-17: UP035 (pyupgrade): typing.Dict is deprecated, use dict instead (imports typing.Dict in from typing ...).


[error] 17-17: UP035 (pyupgrade): typing.List is deprecated, use list instead (imports typing.List in from typing ...).


[error] 53-55: UP006 (pyupgrade): Use dict instead of Dict for type annotation. metadata: Optional[Dict[str, Any]].


[error] 54-56: UP006 (pyupgrade): Use dict instead of Dict for type annotation. transcript: Optional[Dict[str, Any]].


[error] 55-57: UP006 (pyupgrade): Use dict instead of Dict for type annotation. ai_analysis: Optional[Dict[str, Any]].


[error] 67-67: UP006 (pyupgrade): Use dict instead of Dict for type annotation. metadata: Optional[Dict[str, Any]].


[error] 71-71: UP006 (pyupgrade): Use list instead of List for type annotation. video_urls: List[str].


[error] 372-372: F841: Local variable firestore_service is assigned to but never used.


[error] 402-402: F841: Local variable vertex_service is assigned to but never used.

🪛 GitHub Actions: CI / lint-python
src/youtube_extension/backend/cloud_api_endpoints.py

[error] 12-31: I001 Import block is un-sorted or un-formatted. Organize imports.


[error] 17-17: UP035 typing.Dict is deprecated, use dict instead.


[error] 17-17: UP035 typing.List is deprecated, use list instead.


[error] 53-56: UP006 Use dict instead of Dict for type annotation (Optional Dict fields: metadata, transcript, ai_analysis).


[error] 71-72: UP006 Use list instead of List for type annotation. Replace video_urls: List[str] with video_urls: list[str].


[error] 372-372: F841 Local variable firestore_service is assigned to but never used.


[error] 402-402: F841 Local variable vertex_service is assigned to but never used.

🪛 GitHub Actions: CI / test
src/youtube_extension/backend/real_api_endpoints.py

[error] 122-122: Real API processing failed during POST /api/v2/process-video: RuntimeError('crash'). Request returned HTTP 500.

🔇 Additional comments (2)
src/youtube_extension/backend/cloud_api_endpoints.py (1)

254-260: LGTM!

Also applies to: 288-294, 326-332

src/youtube_extension/backend/real_api_endpoints.py (1)

145-149: LGTM!

Also applies to: 249-253, 382-386

Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread src/youtube_extension/backend/real_api_endpoints.py
…e leak

Addresses CodeRabbit's changes-requested review on the 500-sanitization:

- Add exc_info=True to every sanitized 500 handler in cloud_ai_routes,
  cloud_api_endpoints and real_api_endpoints so the full traceback is
  actually preserved server-side (the PR claimed this but several handlers
  logged only str(e)).
- real_api_endpoints: re-raise HTTPException before the broad except in
  batch_process_videos and search_youtube_videos, so the deliberate 400
  validation errors (>20 videos / >50 results) are no longer swallowed and
  rewrapped as 500. Tighten the batch test to assert a clean 400.
- cloud_api_endpoints task handler: stop persisting str(e) as the task's
  error_message. get_video_status / get_video_result echo error_message to
  clients, so a raw exception there re-exposed internal detail (CWE-209)
  even though the immediate 500 body was already generic. Persist a static
  'Internal server error' and log the real exception with exc_info=True.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens FastAPI error handling by sanitizing HTTP 500 responses so clients no longer receive exception messages, stack-adjacent context, or request-derived values via HTTPException(..., detail=...), while relying on server-side logging for diagnostics.

Changes:

  • Replaced dynamic 500 detail payloads (including dict-shaped details) with the static string "Internal server error" across cloud + real API endpoints.
  • Added/updated unit tests to assert the sanitized 500 response body and introduced a source-scan regression guard test.
  • Updated one existing cloud-route test assertion to match the new sanitized behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/youtube_extension/backend/cloud_ai_routes.py Sanitizes multiple 500 HTTPException.detail strings in cloud AI routes.
src/youtube_extension/backend/cloud_api_endpoints.py Sanitizes 500 details for cloud processing endpoints (including task handler).
src/youtube_extension/backend/real_api_endpoints.py Sanitizes 500 details for real API endpoints.
tests/unit/test_500_info_disclosure.py Adds a regression-guard source scanner intended to prevent reintroducing dynamic 500 details.
tests/unit/test_cloud_routes.py Updates assertion to expect "Internal server error" on a 500 path.
tests/unit/test_real_api_endpoints.py Updates 500-path test to assert no internal state/exception text is leaked.

Comment thread src/youtube_extension/backend/real_api_endpoints.py Outdated
Comment thread src/youtube_extension/backend/cloud_api_endpoints.py Outdated
Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread src/youtube_extension/backend/cloud_ai_routes.py
Comment thread tests/unit/test_500_info_disclosure.py Outdated

Copy link
Copy Markdown
Owner Author

Addressed the review in 4d14e96:

  1. Preserve tracebacks — added exc_info=True to every sanitized 500 handler flagged in cloud_ai_routes.py (provider-status, config/unexpected-analysis, batch, multi-provider), cloud_api_endpoints.py (cloud-processing + task handler), and real_api_endpoints.py (real-API processing). Several previously logged only str(e).
  2. Keep explicit 4xx — added except HTTPException: raise before the broad except in batch_process_videos (>20 videos → 400) and search_youtube_videos (>50 results → 400), which were being swallowed and rewrapped as 500. Tightened test_batch_with_more_than_20_videos_returns_400 to assert the clean 400.
  3. Persisted-state leak (outside-diff, cloud_api_endpoints.py ~221) — good catch: error_msg held str(e) and was written to task state, which get_video_status/get_video_result echo back to clients (CWE-209 via polling). Now persists a static "Internal server error" and logs the real exception with exc_info=True; corrected the inaccurate comment.

Verified locally: test_real_api_endpoints.py + test_500_info_disclosure.py → 82 passed; ruff on the three files is unchanged (lint-neutral — the remaining UP006/I001/F841 are pre-existing repo-wide debt in untouched lines).

Not addressed (out of scope): the trivy check fails with JOB_STATUS_CONFIGURATION_ERROR (scans image eventrelay:test that no prior step builds) and the repo-wide lint-python debt (incl. advanced_video_routes.py, not touched here) — both pre-existing and failing independently of this PR.

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR metadata and changed files =='
gh pr view 814 --json headRefOid,baseRefOid,files --jq '{headRefOid, baseRefOid, files: [.files[].path]}'

echo
echo '== Diff against PR base =='
gh pr diff 814 -- src/youtube_extension/backend/cloud_ai_routes.py \
  src/youtube_extension/backend/cloud_api_endpoints.py \
  src/youtube_extension/backend/real_api_endpoints.py \
  tests/unit/test_real_api_endpoints.py \
  tests/unit/test_500_info_disclosure.py || true

echo
echo '== Module outlines =='
ast-grep outline src/youtube_extension/backend/cloud_ai_routes.py --items all --type function || true
ast-grep outline src/youtube_extension/backend/cloud_api_endpoints.py --items all --type function || true
ast-grep outline src/youtube_extension/backend/real_api_endpoints.py --items all --type function || true

echo
echo '== 500 responses, broad handlers, HTTPException propagation, and task error state =='
rg -n -C 5 -e 'status_code\s*=\s*500' \
  -e 'except HTTPException' \
  -e 'except Exception' \
  -e 'error_msg' \
  -e '\["error"\]|\["message"\]|error_message' \
  src/youtube_extension/backend/cloud_ai_routes.py \
  src/youtube_extension/backend/cloud_api_endpoints.py \
  src/youtube_extension/backend/real_api_endpoints.py

echo
echo '== Targeted test assertions =='
fd -a 'test_(500_info_disclosure|real_api_endpoints|cloud_api_endpoints)\.py$' tests -x sh -c \
  'echo "== $1 =="; rg -n -C 4 -e "more_than_20|does_not_leak|Internal server error|video_url|HTTPException" "$1"' sh {}

Length of output: 56856


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="$(gh pr view 814 --json baseRefOid --jq .baseRefOid)"
head="$(gh pr view 814 --json headRefOid --jq .headRefOid)"

echo '== Complete PR diff =='
git diff --unified=50 "$base...$head" -- \
  src/youtube_extension/backend/cloud_ai_routes.py \
  src/youtube_extension/backend/cloud_api_endpoints.py \
  src/youtube_extension/backend/real_api_endpoints.py \
  tests/unit/test_500_info_disclosure.py \
  tests/unit/test_real_api_endpoints.py \
  tests/unit/test_cloud_routes.py

echo
echo '== Exact route and test contexts =='
sed -n '150,185p' src/youtube_extension/backend/real_api_endpoints.py
sed -n '390,443p' src/youtube_extension/backend/real_api_endpoints.py
sed -n '790,815p' tests/unit/test_real_api_endpoints.py
sed -n '1,115p' tests/unit/test_500_info_disclosure.py

echo
echo '== Every 500 HTTPException call form (keyword and positional) =='
rg -n -U -C 2 --glob '*.py' \
  'HTTPException\(\s*(?:status_code\s*=\s*500|500\s*,)|status_code\s*=\s*500' \
  src/youtube_extension/backend tests

echo
echo '== Dynamic error text in client response/state construction =='
rg -n -C 3 --glob '*.py' \
  '(detail|error|error_message)\s*[=:]\s*(?:str\(|f["'\''].*\{.*(?:e|exc|error).*\}|[a-zA-Z_][a-zA-Z0-9_]*error[a-zA-Z0-9_]*)' \
  src/youtube_extension/backend

Length of output: 1775


🏁 Script executed:

#!/bin/bash
set -u -o pipefail

echo '== PR file patches =='
gh api --paginate 'repos/groupthinking/EventRelay/pulls/814/files?per_page=100' \
  --jq '.[] | select(.filename == "src/youtube_extension/backend/cloud_ai_routes.py" or .filename == "src/youtube_extension/backend/cloud_api_endpoints.py" or .filename == "src/youtube_extension/backend/real_api_endpoints.py" or .filename == "tests/unit/test_500_info_disclosure.py" or .filename == "tests/unit/test_real_api_endpoints.py" or .filename == "tests/unit/test_cloud_routes.py") | "\n===== \(.filename) =====\n\(.patch // "[patch unavailable]")"'

echo
echo '== Exact route and test contexts =='
sed -n '150,185p' src/youtube_extension/backend/real_api_endpoints.py
sed -n '390,443p' src/youtube_extension/backend/real_api_endpoints.py
sed -n '790,815p' tests/unit/test_real_api_endpoints.py
sed -n '1,115p' tests/unit/test_500_info_disclosure.py

echo
echo '== Every 500 HTTPException call form (keyword and positional) =='
rg -n -U -C 2 --glob '*.py' \
  'HTTPException\(\s*(?:status_code\s*=\s*500|500\s*,)|status_code\s*=\s*500' \
  src/youtube_extension/backend tests || true

echo
echo '== Dynamic error text in client response/state construction =='
rg -n -C 3 --glob '*.py' \
  '(detail|error|error_message)\s*[=:]\s*(?:str\(|f["'\''].*\{.*(?:e|exc|error).*\}|[a-zA-Z_][a-zA-Z0-9_]*error[a-zA-Z0-9_]*)' \
  src/youtube_extension/backend || true

Length of output: 74637


@groupthinking: The handler changes are correct: the reviewed 500 paths now return a static response, preserve server-side tracebacks, retain the explicit 400s, and no longer persist raw exception text into Cloud Task state.

Two regression-test gaps remain:

  1. test_max_results_above_50_returns_error still accepts a 500.
    The route now correctly re-raises the explicit 400, but Line 805–809 of tests/unit/test_real_api_endpoints.py allows either status. Tighten it to assert response.status_code == 400 and assert the expected limit message, matching the batch-limit test. Otherwise, a future broad except regression will pass CI.

  2. The new 500 disclosure guard has a material bypass.
    tests/unit/test_500_info_disclosure.py only recognizes status_code=500 plus detail=str(...) or an f-string. It does not detect positional calls such as the existing HTTPException(500, str(e)) instances in src/youtube_extension/backend/api/advanced_video_routes.py. Consequently, the guard passes while active dynamic 500 disclosures remain in the backend. Extend the scanner (preferably AST-based) to recognize positional HTTPException(500, <dynamic detail>) and variable-derived detail= values, then add synthetic tests for each supported form.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026
Closes the two regression-test gaps CodeRabbit/Copilot flagged:

- Rewrite test_500_info_disclosure guard from a keyword-only regex to an AST
  scan. It now flags every 500 HTTPException whose detail is not a static
  string literal — positional HTTPException(500, str(e)), keyword
  detail=str(e)/f-strings, dicts, and bare variables (detail=error_msg) — and
  is explicitly scoped to the routers this PR hardens (cloud_ai_routes,
  cloud_api_endpoints, real_api_endpoints), with a docstring that no longer
  over-claims tree-wide coverage. Legacy leaks elsewhere (e.g.
  api/advanced_video_routes.py) are out of scope and tracked separately.
  Synthetic self-checks cover each supported form plus static/4xx negatives.
- Tighten test_max_results_above_50 to assert a clean 400 + limit message
  (was accepting 400 or 500), matching the batch-limit test now that the
  search handler re-raises HTTPException before its broad except.

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

Flagged by Copilot and Vercel's agent (VADE, security): the video processor's
normal failure path persists raw exception text (str(e)) into processing state,
which /status, /result, and the sync process-video response echoed back to
clients via error_message/error (CWE-209) — the endpoint-level 500 handler only
covered the raise path, not this persisted path.

Add _client_safe_error() and apply it at all three client boundaries in
cloud_api_endpoints.py: clients now get a generic 'Internal server error' when a
failure occurred, while the full message stays in server-side state and logs.
Fixing at the boundary covers every persistence path without changing the
processor module or its internal-facing result.error_message (which its own
tests assert). Update test_process_video_sync_failed to assert the sanitized
body and that the raw message never appears in the response.

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

Copy link
Copy Markdown
Owner Author

Update: follow-up (2) is now fixed too, not deferred — Vercel's agent also flagged it as a security leak, so with two reviewers concurring I brought it in scope via the response-boundary approach Copilot suggested (keeps the fix inside cloud_api_endpoints.py, no processor-module change).

In 52f0a26, added _client_safe_error() and applied it to all three client boundaries — the sync process-video response (error), GET /status, and GET /result (error_message). Clients now receive a generic "Internal server error" whenever a failure occurred; the raw persisted text stays in server-side state and logs. This covers every persistence path (both the task handler's raise path and the processor's normal internal-catch path), not just the raise path. Updated test_process_video_sync_failed to assert the sanitized body and that the raw message never appears in the response.

With this, all review findings from CodeRabbit, Copilot, and Vercel's agent are addressed. No deferred items remain.


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread tests/unit/test_500_info_disclosure.py Outdated
Copilot: the guard scans 500s only and the endpoint tests asserted status
codes alone, so a regression re-leaking str(e) in the newly sanitized 429
(RateLimitError) or 503 (CloudAIError) branch would pass unnoticed.

Assert the exact static bodies and the absence of the exception text in
test_analyze_video_rate_limit_error (429 -> 'Rate limit exceeded') and
test_analyze_video_cloud_ai_error (503 -> 'AI service temporarily
unavailable'). Also replace the guard's misleading dynamic-429 'safe' control
with a genuine client-input 4xx example and note where 429/503 coverage lives.

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

# Conflicts:
#	tests/unit/test_cloud_routes.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026

@groupthinking groupthinking left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the latest head and don’t see a remaining correctness or security issue in the changed paths.

What changed here is solid:

  • 500 responses in the touched routers are now sanitized instead of echoing exception text.
  • the analyze_video exception ordering is fixed so RateLimitError and ConfigurationError no longer fall through the base CloudAIError handler.
  • persisted failure state is sanitized at the response boundary, which closes the polling/status leak.
  • regression coverage was tightened so the 400/429/500/503 paths now assert sanitized bodies instead of only status codes.

One remaining caveat: GitHub still reports this PR as not cleanly mergeable right now, so I’d resolve that before landing.

Net: no blocking code issue from me on the current diff; the remaining work looks like branch/merge hygiene rather than logic changes.

@groupthinking
groupthinking dismissed coderabbitai[bot]’s stale review July 17, 2026 09:28

The merge-base changed after approval.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 17, 2026

Copy link
Copy Markdown
Owner Author

⚠️ Recommend closing this as superseded by main — do not force-merge it.

Since the review, main advanced ~111 commits (now d301d9d) and independently landed a superset of this PR's security work. I diffed the current head against main:

  • cloud_ai_routes.pymain already has the exact analyze_video exception-ordering fix (RateLimitError→429, ConfigurationError→sanitized 500, CloudAIError→sanitized 503) and sanitized 500 details throughout. Essentially identical to this PR.
  • tests/unit/test_500_info_disclosure.pymain's guard is strictly more advanced than this PR's: it's AST-based and covers JSONResponse 500 leaks (exception/request references, leaky attributes), not just HTTPException. Merging this PR would regress that guard back to the HTTPException-only version.
  • cloud_api_endpoints.py / processor — the module this PR's response-boundary fix targeted, services/cloud_video_processor.py, no longer exists on main (restructured into real_ai_processor.py et al.). This PR's _client_safe_error change doesn't map cleanly onto the new code.

Net: merging #814 would conflict against restructured code and downgrade main's superior disclosure guard, for ~zero net security benefit — the hardening is already in main. This is the same situation as #807.

Recommendation: disable auto-merge and close #814 as superseded (I've left it unmerged and unresolved so auto-merge stays safely stalled on the conflict — nothing will land by accident).

One residual worth a fresh follow-up (not this PR): main still returns error=result.error_message and error_message=state.error_message raw at the sync process-video response and GET /status //result, while real_ai_processor.py persists exception-derived error_message. If that raw text is client-reachable, it's a live CWE-209 boundary leak — but it should be fixed with a small PR against current main, not by landing this stale branch.

Happy to open that follow-up or close this out on your say-so.


Generated by Claude Code

@groupthinking
groupthinking dismissed coderabbitai[bot]’s stale review July 17, 2026 09:37

The merge-base changed after approval.

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

auto-merge was automatically disabled July 19, 2026 05:00

Pull request was closed

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

Labels

high-priority Urgent - blocks revenue or core functionality jules python security tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants