T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/yt_utils.py:1126
- Finding
- Unrestricted LLM Endpoint Can Exfiltrate API Credentials and Transcript Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/yt_utils.py`, lines 1126-1144 **Vulnerability Type**: Unrestricted credential-bearing network destination and server-side request forgery **Risk Level**: High ### Vulnerable Code ```python def _call_openai_compatible(prompt, provider_cfg, timeout_s, temperature, max_tokens, default_url, auth_header=True): model = str(provider_cfg.get("model", "")).strip() base_url = str(provider_cfg.get("base_url", "")).strip() or default_url api_key_env = str(provider_cfg.get("api_key_env", "")).strip() api_key = os.environ.get(api_key_env, "") if api_key_env else "" headers = {"Content-Type": "application/json"} if auth_header: if not api_key: return None headers["Authorization"] = "Bearer {0}".format(api_key) payload = { "model": model, "messages": [{"role": "user", "content": prompt}], "temperature": temperature, "max_tokens": max_tokens, } try: data = _http_post_json(base_url, payload, headers=headers, timeout_s=timeout_s) except Exception: return None ``` Equivalent unrestricted `base_url` behavior is also present for Anthropic at lines 1168-1188 and Gemini at lines 1203-1232. ### Technical Analysis The provider configuration accepts an arbitrary `base_url`. The code reads an API credential from the environment, places it in an authorization header, and transmits it to that URL without: - Validating the destination hostname against the selected provider. - Requiring HTTPS for credential-bearing remote requests. - Rejecting loopback, link-local, or private-network destinations. - Requiring explicit confirmation before sending data to a custom endpoint. - Preventing an official-provider credential from being reused with an unrelated host. The request payload also contains the LLM prompt. During summary generation, this prompt includes complete transcript chunks. During tagging, it includes ...[truncated 2083 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Allowlist the official origin and path for each cloud provider: - OpenAI: `https://api.openai.com` - Anthropic: `https://api.anthropic.com` - Gemini: `https://generativelanguage.googleapis.com` - OpenRouter: `https://openrouter.ai` 2. Reject custom destinations by default. Require an explicit option such as `allow_custom_endpoint: true`. 3. Require HTTPS for every remote endpoint that receives credentials. Permit plain HTTP only for verified loopback Ollama addresses such as `127.0.0.1`, `localhost`, or `::1`. 4. Do not send official-provider credentials to custom origins. Use a separate custom-endpoint credential setting and environment variable. 5. Parse URLs with `urllib.parse.urlsplit` and reject embedded credentials, unexpected schemes, malformed hosts, link-local addresses, and private-network destinations unless explicitly authorized. 6. Resolve and display the effective destination before transmitting transcript content, and obtain informed user consent for non-local providers. 7. Warn users that cloud summarization sends transcript content to the selected provider. 8. Avoid placing API keys in URL query strings. Gemini credentials should always be carried in an appropriate request header. 9. Consider disabling automatic redirects or revalidating every redirect destination before forwarding credentials. ]]>
