T09 · Insecure Skill Coding Practices
Error
- Location
- client.py:33
- Finding
- API Key Disclosure Through Arbitrary Request Destinations## Vulnerability Details **File Location**: `client.py:33-36`, `client.py:49-55`, and `client.py:126-130` **Vulnerability Type**: Credential disclosure caused by insufficient destination validation **Risk Level**: High ### Vulnerable Code ```python def resolve_url(spec): url = spec.get("url") if url: return url path = spec.get("path") if not path: die("request spec requires either 'path' or 'url'") base_url = os.getenv("PUGOING_BASE_URL", "http://127.0.0.1:8080").rstrip("/") if not path.startswith("/"): path = "/" + path return base_url + path ``` ```python def build_headers(spec): headers = {"Accept": "application/json"} headers.update(spec.get("headers") or {}) api_key = os.getenv("PUGOING_API_KEY", "").strip() if api_key and "X-API-Key" not in headers: headers["X-API-Key"] = api_key return headers ``` ```python try: with urllib.request.urlopen(req, timeout=timeout) as resp: content_type = resp.headers.get("Content-Type", "") if "text/event-stream" in content_type or final_url.endswith("/api/ai/chat"): payload = collect_sse_response(resp) else: payload = parse_json_or_text(resp.read()) ``` ### Technical Analysis The request specification may provide a complete URL through the `url` field. `resolve_url()` accepts that URL without validating its scheme, hostname, port, or relationship to `PUGOING_BASE_URL`. Independently, `build_headers()` automatically adds the secret from `PUGOING_API_KEY` to every request unless an `X-API-Key` header was already supplied. Consequently, the credential is attached even when the selected destination is unrelated to the configured Pugoing server. This violates credential scoping requirements: service credentials must only be released to an explicitly trusted origin. The client also relies on the standard URL ope ...[truncated 1267 chars]
- Remediation
- ## Remediation Suggestions 1. Remove support for arbitrary complete URLs unless it is strictly required. 2. Resolve all requests relative to `PUGOING_BASE_URL`. 3. If complete URLs must remain supported, parse both URLs with `urllib.parse.urlsplit()` and require an exact match of the trusted scheme, normalized hostname, and effective port. 4. Permit only `https` destinations, except for explicitly validated loopback development endpoints. 5. Reject URLs containing user information, unexpected fragments, unsupported schemes, or ambiguous host representations. 6. Add `X-API-Key` only after the final destination has passed origin validation. 7. Disable redirects or implement a redirect handler that rejects every cross-origin redirect. 8. Consider maintaining an explicit hostname allowlist for deployments that use multiple trusted API endpoints. 9. Add tests confirming that attacker-controlled external URLs and cross-origin redirects never receive the API key.
