T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/topmediai_tts_api.py:20
- Finding
- Unvalidated API Base URL Can Redirect Credentials and TTS Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/topmediai_tts_api.py:20`, `scripts/topmediai_tts_api.py:63-67`, and `scripts/topmediai_tts_api.py:109-124` **Vulnerability Type**: Unvalidated credential-bearing outbound request destination **Risk Level**: Medium ### Vulnerable Code ```python BASE_URL = os.environ.get("TOPMEDIAI_BASE_URL", "https://api.topmediai.com") ``` ```python def _headers(api_key: Optional[str] = None) -> Dict[str, str]: key = api_key or DEFAULT_KEY if not key: raise RuntimeError( "TOPMEDIAI_API_KEY not configured. Edit: {} and set TOPMEDIAI_API_KEY=YOUR_KEY.".format(_ENV_PATH) ) return {"x-api-key": key, "Content-Type": "application/json"} ``` ```python def text_to_speech(text: str, speaker: str, emotion: Optional[str] = None, api_key: Optional[str] = None) -> Dict[str, Any]: url = f"{BASE_URL}/v1/text2speech" headers = _headers(api_key) payload: Dict[str, Any] = { "text": text, "speaker": speaker, } if emotion: payload["emotion"] = emotion _debug_request("POST", url, headers=headers, payload=payload) try: r = requests.post(url, json=payload, headers=headers, timeout=120) r.raise_for_status() return r.json() except Exception as e: _raise_as_runtime_error(e, "POST", url) ``` The same base URL and credential-bearing headers are also used by the account-information and voice-list requests. ### Technical Analysis `TOPMEDIAI_BASE_URL` is accepted directly from the process environment or project `.env` file without validating its scheme, hostname, port, or embedded credentials. The application subsequently attaches the TopMediai API key as an `x-api-key` header to requests sent to that destination. Consequently, anyone able to alter the process environment or `.env` configuration can redirect requests to an attacker-controlled endpoint. For TTS operations, both the API key and the user-supplied tex ...[truncated 1712 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Restrict production requests to the documented TopMediai endpoint: - Allowlist `api.topmediai.com`. - Require the `https` scheme. - Reject embedded credentials, unexpected ports, fragments, and unapproved hostnames. 2. Parse and validate the URL with `urllib.parse.urlparse` before constructing any request. 3. If custom endpoints are required for testing, make them an explicit development-only option and require a separate non-production API key. 4. Fail closed when validation fails instead of sending a request. 5. Consider disabling redirects or validating every redirect target so credentials cannot be forwarded to an unexpected host. 6. Document that anyone able to modify `.env` can control the destination of sensitive requests. 7. Add automated tests covering HTTP URLs, lookalike domains, embedded credentials, unexpected ports, and redirect behavior. Example hardening approach: ```python from urllib.parse import urlparse ALLOWED_API_HOSTS = {"api.topmediai.com"} def validate_base_url(value: str) -> str: parsed = urlparse(value) if parsed.scheme != "https": raise RuntimeError("TOPMEDIAI_BASE_URL must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise RuntimeError("TOPMEDIAI_BASE_URL host is not approved") if parsed.username or parsed.password or parsed.fragment: raise RuntimeError("TOPMEDIAI_BASE_URL contains unsupported components") if parsed.port not in (None, 443): raise RuntimeError("TOPMEDIAI_BASE_URL uses an unapproved port") return value.rstrip("/") BASE_URL = validate_base_url( os.environ.get("TOPMEDIAI_BASE_URL", "https://api.topmediai.com") ) ``` ]]>
