T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/wait_for_task.py:32
- Finding
- API Token Exposed in HTTP Query Parameters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py:32-33, 105-110, 124-128` **Duplicate Location**: `_dependencies/skills/dataify-task-operations/scripts/wait_for_task.py:32-33, 105-110, 124-128` **Vulnerability Type**: Sensitive 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 function is invoked with the long-lived API token included in `params`: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The same behavior occurs when downloading completed task results: ```python if status == SUCCESS_STATUS: return request_json( DOWNLOAD_ENDPOINT, {"api_key": api_key, "task_id": task_id, "type": "json"}, api_key, request_timeout, ) ``` ### Technical Analysis The polling implementation encodes `DATAIFY_API_TOKEN` directly into request URLs, producing requests such as: ```text https://scraperapi.dataify.com/task_status?api_key=<token>&task_id=<task-id> ``` Although HTTPS protects the request while it is in transit, query strings are routinely captured by web server access logs, reverse proxies, API gateways, monitoring products, network debugging tools, and error telemetry. Consequently, placing a long-lived credential in the URL substantially increases its exposure surface. The response-body replacement performed elsewhere in the function only redacts the token if it appears in the response. It does not remove the token from the outbound request URL or from infrastructure logs. This behavior also conflicts with the Skill's documented safety requirement not to expose tokens in logs. Authentication is necessary for task monitoring, but placing the credential in the URL exceeds t ...[truncated 1269 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `api_key` from all query parameter dictionaries. 2. Authenticate using 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": "Bearer {}".format(api_key)}, method="GET", ) ``` 3. If the provider does not support authorization headers, prefer a POST request with the credential in the body and explicitly configure all infrastructure to redact that field. 4. Ensure application, gateway, reverse-proxy, and observability logs redact authentication headers and sensitive parameters. 5. Avoid including credentials in exception messages, debug output, resume commands, or dry-run output. 6. Apply the same correction to both copies of `wait_for_task.py` to prevent the vulnerable bundled dependency from remaining executable. 7. Rotate API tokens that may already have been transmitted through query strings, and review historical logs for unintended credential retention. ]]>
