T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/zoodata.py:55
- Finding
- Bearer API Credential May Be Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/zoodata.py:55-82` and `scripts/zoodata.py:339-378` **Vulnerability Type**: Insufficient transport security validation **Risk Level**: High ### Vulnerable Code ```python def _host_of(url): try: from urllib.parse import urlparse return (urlparse(url if "://" in url else f"https://{url}").hostname or "").lower() except Exception: return "" 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}" BASE_URL = _resolve_base_url() BASE_URL_TRUSTED = _is_trusted_host(BASE_URL) ``` The accepted URL is subsequently used with the bearer credential: ```python 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_a ...[truncated 2789 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require HTTPS for all non-loopback destinations: ```python from urllib.parse import urlparse def _is_trusted_base_url(url): try: parsed = urlparse(url) except ValueError: return False host = (parsed.hostname or "").lower() if parsed.username is not None or parsed.password is not None: return False if parsed.fragment or parsed.query: return False if host in ("localhost", "127.0.0.1"): return parsed.scheme == "https" return ( parsed.scheme == "https" and host == "api.zoodata.ai" and parsed.port in (None, 443) ) ``` 2. Restrict production credential transmission to the exact origin `https://api.zoodata.ai:443` rather than every `*.zoodata.ai` hostname. 3. Remove `ZOODATA_BASE_URL` in production builds if endpoint replacement is not required for the declared functionality. 4. If local development endpoints are necessary, require a separate explicit development flag and a separate non-production API key. Do not send production credentials to localhost over HTTP. 5. Disable automatic redirects for authenticated requests or validate every redirect target before following it. Strip the `Authorization` header whenever the scheme, host, or port changes. 6. Reject malformed URLs, embedded user information, unexpected ports, query strings, and fragments. 7. Add tests confirming rejection of: - `http://api.zoodata.ai` - `http://subdomain.zoodata.ai` - `https://api.zoodata.ai:444` - URLs containing username or password components - Cross-origin and HTTPS-to-HTTP redirects 8. Update `SKILL.md` so its declared network policy exactly matches the implemented allowlist. ]]>
