T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/api_client.py:24
- Finding
- Custom API Key May Be Disclosed Through Cross-Origin HTTP Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/api_client.py`, lines 24–53 **Vulnerability Type**: Credential disclosure through unconstrained redirects **Risk Level**: Medium ### Complete Code Snippet ```python def get_headers() -> Dict[str, str]: """ Get HTTP headers for API requests. Reads CLARITY_API_KEY from environment variable if present. Returns: Dict with Accept header and optional X-API-Key header """ headers = { "Accept": "application/json" } api_key = os.environ.get("CLARITY_API_KEY") if api_key: headers["X-API-Key"] = api_key return headers def api_get(endpoint: str, params: Optional[Dict[str, Any]] = None) -> Any: """ Perform GET request to Clarity Protocol API with error handling. """ url = API_BASE + endpoint try: response = requests.get( url, params=params, headers=get_headers(), timeout=30 ) ``` ### Technical Analysis The client places the `CLARITY_API_KEY` credential in a custom `X-API-Key` request header and calls `requests.get()` without configuring redirect handling. The Requests library follows redirects by default. Requests has special handling that can remove the standard `Authorization` header when a redirect crosses origin boundaries. That protection does not automatically apply to arbitrary authentication headers such as `X-API-Key`. Consequently, the custom API key can remain attached when the client follows a redirect from `clarityprotocol.io` to a different host. Network access and transmission of the API key to the declared Clarity Protocol service are necessary and documented aspects of the Skill. Forwarding that credential to an arbitrary redirected origin is not necessary for the declared clinical-variant query functionality and exceeds the minimum required credential scope. Exploitation requires the Clarity endpoint or its response path to produce an ...[truncated 1406 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Disable automatic redirects for authenticated API requests: ```python response = requests.get( url, params=params, headers=get_headers(), timeout=30, allow_redirects=False, ) ``` 2. If redirects are required, process them manually and validate every destination before following it: - Require the `https` scheme. - Compare parsed hostnames exactly rather than using suffix or substring checks. - Permit only an explicit allowlist of trusted Clarity Protocol hosts. - Reject redirects containing user information, unexpected ports, or unapproved domains. 3. Never copy `X-API-Key` to a redirected request when the scheme, hostname, or port changes. Rebuild headers for each redirect and include the credential only when the destination is the exact approved API origin. 4. Apply a strict redirect limit to prevent redirect loops and unexpected request chains. 5. Add automated tests that return same-origin and cross-origin redirects and verify that `X-API-Key` is absent from every request sent outside the approved origin. A hardened implementation should use a dedicated session and explicitly reject cross-origin redirects: ```python from urllib.parse import urlparse ALLOWED_ORIGIN = ("https", "clarityprotocol.io", 443) response = requests.get( url, params=params, headers=get_headers(), timeout=30, allow_redirects=False, ) if response.is_redirect: location = response.headers.get("Location", "") target = urlparse(location) target_port = target.port or (443 if target.scheme == "https" else None) if (target.scheme, target.hostname, target_port) != ALLOWED_ORIGIN: raise RuntimeError("Refusing redirect to an untrusted origin") ``` ]]>
