T09 · Insecure Skill Coding Practices
- Location
scripts/_common.py:32- Finding
Bearer Token Can Be Transmitted to an Arbitrary or Plaintext Endpoint
- Content
View full analysis
str: return os.environ.get("ZOPIA_BASE_URL", "https://zopia.ai").rstrip("/") def _build_headers() -> dict[str, str]: return { "Authorization": f"Bearer {_get_access_key()}", "Content-Type": "application/json", } def api_get(path: str, params: dict[str, str] | None = None) -> Any: url = f"{_get_base_url()}{path}" if params: url = f"{url}?{urllib.parse.urlencode(params)}" req = urllib.request.Request(url, headers=_build_headers(), method="GET") return _do_request(req) def api_post(path: str, body: dict[str, Any] | None = None) -> Any: url = f"{_get_base_url()}{path}" data = json.dumps(body or {}).encode("utf-8") req = urllib.request.Request(url, data=data, headers=_build_headers(), method="POST") return _do_request(req) def api_delete(path: str) -> Any: url = f"{_get_base_url()}{path}" req = urllib.request.Request(url, headers=_build_headers(), method="DELETE") return _do_request(req) ``` ### Technical Analysis The destination for every authenticated API request is derived directly from the `ZOPIA_BASE_URL` environment variable. The code does not validate: - Whether the URL uses HTTPS. - Whether the hostname belongs to Zopia. - Whether the destination is a loopback or private-network address. - Whether a redirect changes the request destination to an untrusted origin. The same request attaches `ZOPIA_ACCESS_KEY` as a bearer credential. Consequently, an attacker who can influence the inherited environment can direct authenticated requests to an attacker-controlled endpoint. A plaintext HTTP destination additionally exposes the token and request content to network interception. The project documentation explicitly permits changing ...[truncated 1523 chars]- Remediation
View remediation
