T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/search.py:6
- Finding
- Google API Key Exposure Through Unsanitized HTTP Error Output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search.py:6-15, 33-36` **Vulnerability Type**: Sensitive credential exposure through exception logging **Risk Level**: Medium ### Vulnerable Code ```python def google_search(query, api_key, cse_id, num_results=5): url = "https://www.googleapis.com/customsearch/v1" params = { 'q': query, 'key': api_key, 'cx': cse_id, 'num': num_results } response = requests.get(url, params=params) response.raise_for_status() return response.json() ``` ```python try: results = google_search(query, api_key, cse_id, num_results) print(json.dumps(results, indent=2)) except Exception as e: print(f"Error: {str(e)}") sys.exit(1) ``` ### Technical Analysis The Google API key is supplied through the `key` query-string parameter. The `requests` library incorporates the prepared request URL into the message of an `HTTPError` generated by `response.raise_for_status()`. Because the request URL contains the API key, converting that exception to a string can include the credential. The broad exception handler prints the raw exception without redacting sensitive query parameters. If command output is retained in agent transcripts, centralized logs, CI output, or terminal history, the API key can consequently be disclosed to parties with access to those records. The request also has no timeout, which can cause the process to remain blocked during network failures. This is a reliability concern, although it does not independently establish credential disclosure. ### Attack Path 1. The skill is invoked with valid `GOOGLE_API_KEY` and `GOOGLE_CSE_ID` environment variables. 2. A request results in a non-success HTTP status, for example because of an invalid parameter, quota exhaustion, revoked credentials, or a Google API authorization error. 3. `response.raise_for_status()` raises an `HTTPError` containing the prepared request URL. 4. The prepared URL may ...[truncated 942 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not print raw `requests` exceptions when requests contain credentials. Report only a fixed error message and sanitized status information: ```python try: results = google_search(query, api_key, cse_id, num_results) print(json.dumps(results, indent=2)) except requests.HTTPError as exc: status = exc.response.status_code if exc.response is not None else "unknown" print(f"Google Search API request failed with HTTP status {status}.") sys.exit(1) except requests.RequestException: print("Google Search API request failed due to a network error.") sys.exit(1) ``` 2. If URLs must be logged for diagnostics, parse them and replace sensitive parameters such as `key` with `[REDACTED]` before producing output. 3. Add a finite timeout to prevent indefinite blocking: ```python response = requests.get(url, params=params, timeout=(5, 30)) ``` 4. Restrict the Google API key to the Custom Search API and apply all supported client, quota, and billing restrictions. Avoid enabling unrelated APIs for the same key. 5. Ensure application and agent logs have access controls and retention limits. Search existing logs for exposed keys and rotate the credential if disclosure may already have occurred. 6. Add automated tests that simulate non-success HTTP responses and verify that neither the API key nor the complete credential-bearing URL appears in standard output, standard error, or logs. ]]>
