T09 · Insecure Skill Coding Practices
Warning
- Location
- search.py:12
- Finding
- API Credential Exposed in Request URLs<![CDATA[ ## Vulnerability Details **File Location**: `search.py:12-14` and `search.py:35-37` **Vulnerability Type**: API credential exposure through URL query parameters **Risk Level**: Medium ### Vulnerable Code ```python url = f"https://api.legiscan.com/?key={api_key}&op=getSearch&state={state}&query={query_param}" try: response = requests.get(url).json() ``` ```python url = f"https://api.legiscan.com/?key={api_key}&op=getBill&id={bill_id}" try: res = requests.get(url).json() ``` ### Technical Analysis The Skill reads `LEGISCAN_API_KEY` from the environment and interpolates it directly into URLs sent to the official LegiScan API. Network access and authentication to LegiScan are necessary for the declared bill-tracking functionality, and the requests use HTTPS. Therefore, this behavior is not evidence of unauthorized data exfiltration. However, credentials in URL query parameters can be exposed through: - LegiScan access logs - Forward-proxy or TLS-inspection logs - HTTP debugging and application-performance monitoring systems - Exception telemetry that records request URLs - Shell, test, or diagnostic output added around the HTTP client The user-controlled state and keyword values are also manually interpolated rather than passed through the HTTP client's parameter-encoding interface. Although this does not create a demonstrated command-execution path, it makes request construction less robust. ### Attack Path 1. A user configures `LEGISCAN_API_KEY` and runs the Skill. 2. The Skill constructs a request URL containing the complete API key. 3. A server, proxy, monitoring agent, debugger, or telemetry platform records the requested URL. 4. An attacker or unauthorized operator with access to those records extracts the `key` query parameter. 5. The attacker submits requests to LegiScan using the recovered credential. This path requires access to infrastructure that records request URLs; the project itself does not intentionally print or trans ...[truncated 647 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Use an authentication header instead of a query parameter if the LegiScan API supports one: ```python endpoint = "https://api.legiscan.com/" headers = {"Authorization": f"Bearer {api_key}"} params = { "op": "getSearch", "state": state, "query": query_param, } response = requests.get( endpoint, headers=headers, params=params, timeout=15, ) response.raise_for_status() data = response.json() ``` 2. If LegiScan requires the key in the query string: - Use `params` for correct parameter encoding. - Configure proxies, telemetry, and HTTP debugging tools to redact the `key` parameter. - Never log `response.request.url` without sanitization. - Use a dedicated, revocable API key with the lowest available quota and privileges. - Rotate the key immediately if URLs containing it may have been retained in logs. 3. Add explicit request timeouts and status handling so network failures cannot leave scheduled executions indefinitely blocked: ```python endpoint = "https://api.legiscan.com/" params = { "key": api_key, "op": "getBill", "id": bill_id, } response = requests.get(endpoint, params=params, timeout=15) response.raise_for_status() data = response.json() ``` 4. Validate state input against valid two-letter state codes and reject empty keyword entries before making requests. This reduces malformed or unintended API operations, although it does not replace credential redaction. ]]>
