T09 · Insecure Skill Coding Practices
- Location
scripts/zoodata.py:65- Finding
Bearer Credential May Be Transmitted over Plaintext HTTP
- Content
View full analysis
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: HighVulnerable 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
View remediation
Remediation Suggestions
- Parse the complete configured URL with
urllib.parse.urlparse. - Require
scheme == "https"forzoodata.aiand all permitted subdomains. - Reject URLs containing user-information, malformed ports, fragments, or unexpected path components.
- Prefer a fixed production origin rather than allowing arbitrary ZooData subdomains.
- 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.
- Validate the final constructed URL immediately before every authenticated request, rather than relying only on module-level state.
- Add tests proving that
http://api.zoodata.ai,http://localhost, protocol-relative URLs, and malformed URLs are rejected before credential resolution.
- Parse the complete configured URL with
