T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/zoodata.py:65
- Finding
- Bearer Credential May Be Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `scripts/zoodata.py`, lines 65-84 and 329-362 **Vulnerability Type**: Insufficient URL scheme validation for authenticated API requests **Risk Level**: High ### Vulnerable Code ```python def _is_trusted_host(url): """True only for ZooData hosts and localhost — the sole destinations the API key (Bearer token) may be sent to. Any other host is untrusted and the key is withheld (see api_call), so credentials never reach an arbitrary host.""" host = _host_of(url) return host == "zoodata.ai" or host.endswith(".zoodata.ai") or host in ("localhost", "127.0.0.1") def _resolve_base_url(): """Resolve API base URL, allowing zoodata.ai / localhost hosts via ZOODATA_BASE_URL.""" configured = os.environ.get("ZOODATA_BASE_URL", DEFAULT_BASE_URL).strip().rstrip("/") if configured.rstrip("/") != DEFAULT_BASE_URL.rstrip("/") and not _is_trusted_host(configured): print(f"WARNING: ZOODATA_BASE_URL points at untrusted host '{_host_of(configured)}'. " "Your API key (Bearer token) will NOT be sent there — requests to untrusted " "hosts are refused. Use a zoodata.ai host or localhost.", file=sys.stderr) if configured.endswith(API_BASE_PATH): return configured return f"{configured}{API_BASE_PATH}" ``` ```python def api_call(endpoint: str, params: dict) -> dict: global _last_request_time url = f"{BASE_URL}/{endpoint}" if not BASE_URL_TRUSTED: print(f"ERROR: refusing to send your API key to untrusted host '{_host_of(BASE_URL)}'. " "Set ZOODATA_BASE_URL to a zoodata.ai host or localhost, or unset it.", file=sys.stderr) sys.exit(1) api_key = get_api_key() params = {k: v for k, v in params.items() if v is not None} actual_params = dict(params) body = json.dumps(params).encode("utf-8") headers = ...[truncated 1976 chars]
- Remediation
- ## Remediation Suggestions 1. Parse the complete configured URL with `urllib.parse.urlparse`. 2. Require `scheme == "https"` for `zoodata.ai` and all permitted subdomains. 3. Reject URLs containing user-information, malformed ports, fragments, or unexpected path components. 4. Prefer a fixed production origin rather than allowing arbitrary ZooData subdomains. 5. Disable authenticated plaintext localhost requests by default. If local development support is required, place it behind an explicit development-only flag and use a separate non-production credential. 6. Validate the final constructed URL immediately before every authenticated request, rather than relying only on module-level state. 7. Add tests proving that `http://api.zoodata.ai`, `http://localhost`, protocol-relative URLs, and malformed URLs are rejected before credential resolution.
