fix(security): stop proxy credentials leaking from subprocess/urlparse errors - #1120
fix(security): stop proxy credentials leaking from subprocess/urlparse errors#1120groupthinking wants to merge 2 commits into
Conversation
…1113) `WEBSHARE_PROXY_URL` carries `user:password` in its userinfo. Two paths leaked it verbatim: 1. `enhanced_video_processor._get_openai_whisper_transcript` runs yt-dlp via `subprocess.run(..., check=True)`. The resulting `CalledProcessError` stringifies the whole argv, including `--proxy http://user:pass@host`. That string was written to `logger.warning` (CWE-532) *and* returned to the caller in the `error` field of the response (CWE-209). 2. `robust._get_metadata_ytdlp` raised `yt-dlp failed: {result.stderr}`; yt-dlp echoes the `--proxy` value back on stderr for connection failures. `TimeoutExpired` from the same call site stringifies the argv too. Separately, `get_proxy_url()` documented "malformed => None" but did not honour it: `urllib.parse` raises `ValueError` on an unterminated IPv6 literal at parse time, and on a non-numeric or out-of-range port when `.port` is read. The exception escaped to callers that log it, which put the offending URL — credentials and all — into the log a third way. Changes: - `utils/proxy.get_proxy_url` contains `ValueError` from both `urlparse` and the `.port` access, adds `socks5h` to the allowed schemes, and keeps the URL out of the "malformed" warning. - `utils/proxy.redact_proxy_credentials` now accepts any object, never raises (it runs inside `except` blocks, where a failure would mask the original error), and sweeps in two passes: an exact replacement of the configured env value that preserves host:port for triage, then a generic `scheme://user:pass@` regex for normalised stderr echoes, argv dumps and other proxy variables. The user/password classes exclude `/`, so a path containing `@` is not over-redacted. - Both leak sites redact before logging or returning. - `shared/libs/youtube_proxy.py` (a drifted duplicate, loaded both as a package and standalone via importlib) now delegates to the canonical helper with an equivalent local fallback, matching the pattern already used in `gemini_video_master_agent.py`. This also fixes a latent `UnboundLocalError` on a portless proxy URL. 16 of the 29 new tests in `tests/unit/test_proxy_utils.py` fail against the pre-fix code. Full `tests/unit` run shows no new failures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Both redaction helpers documented a "never raises" contract but called str(text) unguarded. Since the helper runs inside except blocks, an object whose __str__ itself raises would propagate out of the sanitizer, masking the original failure and suppressing the sanitized log/response. Wrap the coercion in try/except and fall back to a fixed, non-sensitive "<unprintable error>" placeholder on failure, in both the canonical helper (src/youtube_extension/utils/proxy.py) and the standalone fallback (shared/libs/youtube_proxy.py). Add a regression test exercising an object with a raising __str__. Addresses CodeRabbit (critical) and Copilot review findings on PR #1118. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PTuvfPb1mbuCq7CRK5v2zS
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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:
Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure 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 FilesNone |
🔍 PR Validation |
Agent Completion Truth Gate: BLOCKEDReasons: Machine-readable verdict{
"details": {
"invalid_fields": [
"policy.agent_login",
"policy.run_id"
]
},
"reasons": [
"invalid_payload"
],
"verdict": "blocked"
} |
Status & blocker (automated remediation run)Tractable CI is green; the one red check is a by-design human governance gate — not a code issue. Verification on head
Blocking check — This is not a defect in this PR. Clearing it is a deliberate human, protected-default-branch action — provisioning Requesting a human decision (product/security/irreversible-infra/production-approval, per the PR template): this PR is intentionally left as a draft. To land it, a maintainer needs to (1) provision the agent-lock trusted publisher (or grant the documented human exemption), (2) mark the PR ready-for-review, and (3) approve the merge to the protected Generated by Claude Code |
|
Closing as a duplicate of #1118, which is the older PR for #1113 and a strict superset of this one. Containment proofThis PR's head Why this had to be resolved
Keeping the superset and closing this one clears that gate. What #1118 adds on topThe Please continue any follow-up review on #1118. |
|
Superseded by #1118 (strict superset, contains this branch's commits). See containment proof above. |
|
Closing as superseded by #1118. Both PRs close #1113 with the same proxy-credential-redaction fix. #1118's head ( Keeping both open trips the "Canonical issue and evidence" gate on #1118 ("Issue #1113 already has another open implementation PR: #1120"). Closing this one clears that gate. No work is lost — it all lives in #1118. Generated by Claude Code |
Canonical issue
Closes #1113
Outcome
Proxy credentials (
WEBSHARE_PROXY_URLuserinfo, and anyscheme://user:pass@hostURL) can no longer leak into logs or returned error fields when a subprocess or URL parse fails. This closes CWE-532 (log injection of secrets) and CWE-209 (secret disclosure via error responses) on the YouTube proxy path.Scope
src/youtube_extension/utils/proxy.py—get_proxy_url()now containsValueErrorfromurllib.parse(unterminated IPv6 literal, non-numeric / out-of-range port) instead of letting it escape to callers that log it; honours the documented "malformed ⇒ direct connection" contract.redact_proxy_credentials()rewritten as two passes: (1) exact replacement of the configured env URL preserving the host for triage, (2) a genericscheme://user:pass@sweep that catches credentials that never match the env value verbatim (yt-dlp stderr echoes,CalledProcessErrorargv dumps, other proxy vars). Accepts non-strinput and never raises.shared/libs/youtube_proxy.py— mirrors the same validation + redaction (this module is also loaded standalone by path, so it keeps an equivalent local fallback).enhanced_video_processor.py/adapters/robust.py— redact before logging/raising on WhisperCalledProcessErrorand yt-dlpTimeoutExpired/ stderr.tests/unit/test_proxy_utils.py(new), plus additions totest_robust_youtube_service.pyandtest_enhanced_video_processor.py.Risk
@in a log line; mitigated by anchoring the regex to ascheme://prefix and excluding/from the user/password classes so paths containing@are not matched.Verification
Tied to head SHA
3076bad5f8e7dd25755a5e781213ebbf08cc7af5:PYTHONPATH=src pytest tests/unit/test_proxy_utils.py tests/unit/test_robust_youtube_service.py tests/unit/test_enhanced_video_processor.py→ 199 passedCalledProcessError) redaction, and malformed-URL→None contract all confirmedruff checkon changed files shows no new errors vs themainbaseline (new test file is clean; pre-existing counts unchanged)agent-completion/truth-gaterequires the PR to leave draft state)Production evidence
Not applicable — this is a backend credential-hygiene fix on the YouTube proxy error path with no user-facing surface or deployable artifact. Behaviour is exercised by the unit regression tests above.
Agent handoff
mainbranchAgent provenance
Prepared by Claude Code under the PR remediation runbook. Kept as a draft pending human review and merge to the protected
mainbranch.Generated by Claude Code