Skip to content

Two unlanded security fixes orphaned by the force-push: proxy credential leak and the ecdsa advisory #1437

Description

@groupthinking

Re-cut of the two security items in #1378. Both original PRs (#1118, #1156) share no ancestry with main after the secret-purge force-push, so neither could be rebased or cherry-picked — the work had to be reimplemented against current main.

Neither fix is present on main today.

1. Proxy credentials leak from logged errors

redact_proxy_credentials bails out unless the configured WEBSHARE_PROXY_URL appears in the text byte-for-byte:

if not url or url not in text:
    return text

It is called from exception handlers that log subprocess and HTTP failures, so anything it skips is written to logs verbatim. Reproduced against current main with WEBSHARE_PROXY_URL=http://user:s3cr3t@proxy.internal:8080:

Text Result
connect to http://user:s3cr3t@PROXY.INTERNAL:8080 (host case-normalised by urllib3) leaked
http://user:s3cr3t%40x@proxy.internal:8080 (percent-encoded, echoed by yt-dlp) leaked
HTTPS_PROXY=http://bob:hunter2@corp.proxy:3128 (different variable entirely) leaked
CalledProcessError: ['yt-dlp','--proxy','http://u:p4ss@h:1'] leaked
http://user:pa@ss@proxy.internal:8080 (raw @ in password) leaked

The last case is #1113: RFC 3986 requires @ in userinfo to be percent-encoded, but real proxy values carry a raw one, and matching to the first @ leaves the password tail behind.

The helper also had no test coverage at all.

Fix

A second, generic scheme://user[:password]@ pass that redacts credentials regardless of which variable they came from or how they were rendered. The exact-match pass runs first and is kept, because it preserves the host so operators can still tell which proxy was in play. The helper now accepts non-str input and never raises — it runs inside except blocks where raising would mask the original error.

Applied to both copies: the canonical src/youtube_extension/utils/proxy.py and the drifted shared/libs/youtube_proxy.py, whose three logger calls in the retry handler are the actual leak site.

2. ecdsa advisory with no patched release

python-jose[cryptography] was declared in pyproject.toml and requirements.txt but is never imported. The only occurrence is inside a string template in backend/code_generator.py — text written into projects the generator emits, which get their own requirements.txt pinning it (line 468). Parsing the tree confirms zero jose imports at AST level in src/, shared/, or scripts/.

Declaring it pulled in ecdsa, whose GHSA-wj6h-64fc-37mp has no patched version — so the advisory could not be resolved by upgrading, only by removing the path to it. python-jose is ecdsa's sole dependent.

Fix

Drop the declaration. uv lock removes ecdsa, python-jose, and rsa (rsa's only dependent was also python-jose; google-auth in this resolution needs cryptography and pyasn1-modules). Generated projects are unaffected.

Acceptance criteria

  • No credential survives redaction for any of the five reproduced cases
  • Redaction does not over-redact: @ in a path, query, or fragment, and a bare host:port, all pass through untouched
  • Redaction never raises, and never returns text it cannot vouch for
  • Both proxy helper copies behave identically
  • ecdsa absent from uv.lock
  • Backend and code_generator still import; full unit suite green

Not included

passlib is the same template-only phantom as python-jose and could be dropped on identical reasoning, but it carries no advisory. Flagged in both manifests rather than removed unilaterally.

Activity

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions