T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/wait_for_task.py:35
- Finding
- API Token Exposed in Task Status and Download Query Strings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py:35-37`, `scripts/wait_for_task.py:103-107`, and `scripts/wait_for_task.py:129-134` **Vulnerability Type**: API credential exposure through URL query parameters **Risk Level**: High ### Vulnerable Code ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request(url, method="GET") ``` The normal task-status request passes the API token as a parameter: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The result-download request repeats the same behavior: ```python return request_json( DOWNLOAD_ENDPOINT, {"api_key": api_key, "task_id": task_id, "type": "json"}, api_key, request_timeout, ) ``` ### Technical Analysis The value of `DATAIFY_API_TOKEN` is embedded directly in the URLs used to poll task status and download results. Although HTTPS encrypts the URL while it is in transit, query strings are routinely recorded by web-server access logs, reverse proxies, API gateways, observability systems, exception telemetry, and other infrastructure components. The response-body replacement performed elsewhere in `request_json()` does not protect the request URL: ```python if api_key: text = text.replace(api_key, "<redacted>") ``` This only redacts a token if it appears in the response body. It does not remove the credential from network request logs or upstream telemetry. The behavior occurs during the default completion workflow because `scripts/task_runtime.py` invokes `wait_for_task()`, which repeatedly performs the affected status request and eventually performs the affected download request. ### Attack Path 1. A user configures `DATAIFY_API_TOKEN` and invokes the documented builder workflow. 2. The task is submitted successfully. 3. The completion runtime calls `wait_f ...[truncated 912 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `api_key` from all query parameters. 2. Authenticate status and download requests through an HTTP authorization header: ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request( url, headers={"Authorization": f"Bearer {api_key}"}, method="GET", ) ``` Call it without adding the token to `params`: ```python request_json( STATUS_ENDPOINT, {"task_id": task_id}, api_key, request_timeout, ) ``` 3. Prefer POST requests if the service supports them and task parameters are considered sensitive. 4. If the remote API currently requires query-string authentication, request a header-authentication endpoint from the provider. 5. Until the API is changed, configure all client, proxy, gateway, server, and observability layers to redact `api_key` query parameters. 6. Add automated tests asserting that serialized request URLs never contain the configured token. 7. Rotate tokens that may already have appeared in logs and apply restricted retention and access controls to historical logs. ]]>
