T09 · Insecure Skill Coding Practices
- Location
scripts/pet_query.py:65- Finding
API Credential Exposure Through URL Query Parameters and Command-Line Arguments
- Content
View full analysis
Vulnerability Details
File Location:
scripts/pet_query.py:65-80,scripts/pet_query.py:115-123, andscripts/pet_query.py:294-297
Vulnerability Type: API credential exposure
Risk Level: MediumVulnerable Code
Search requests place the API key directly in the URL:
python def search_pets(query: str = None, category: str = None, page: int = 1, page_size: int = 10, api_key: str = None) -> dict: """搜索宠物""" if not api_key: return {"success": False, "error": "未提供 API Key"} params = { "key": api_key, "page": page, "page_size": page_size, } if query: params["q"] = query if category: params["category"] = category data = urllib.parse.urlencode(params).encode("utf-8") url = f"{SEARCH_API_URL}?{urllib.parse.urlencode(params)}" try: with urllib.request.urlopen(url, timeout=10) as resp: result = json.loads(resp.read().decode("utf-8"))Detail requests use the same credential transport pattern:
python def get_pet_detail(hash_id: str, api_key: str = None) -> dict: """获取宠物详情""" if not api_key: return {"success": False, "error": "未提供 API Key"} params = { "key": api_key, "hash_id": hash_id, } url = f"{DETAIL_API_URL}?{urllib.parse.urlencode(params)}" try: with urllib.request.urlopen(url, timeout=10) as resp: result = json.loads(resp.read().decode("utf-8"))The key can also be supplied as a command-line argument:
python while i < len(args): if args[i] == "--key" and i + 1 < len(args): cli_key = args[i + 1] i += 2Technical Analysis
The script transmits the Juhe API key in the query component of an HTTPS URL. HTTPS protects the complete request from passive network observers while it is in transit, but it does not prevent the URL fro ...[truncated 2833 chars]
- Remediation
View remediation
Remediation Suggestions
-
Prefer an API-supported authorization header rather than a query parameter, for example:
python params = { "page": page, "page_size": page_size, } url = f"{SEARCH_API_URL}?{urllib.parse.urlencode(params)}" request = urllib.request.Request( url, headers={"Authorization": f"Bearer {api_key}"}, ) with urllib.request.urlopen(request, timeout=10) as resp: result = json.loads(resp.read().decode("utf-8"))The exact header and authentication scheme must match Juhe's official API specification.
-
If Juhe mandates a
keyquery parameter, clearly document this provider constraint and configure all application, proxy, monitoring, and access logging to redact thekeyparameter. -
Use a POST body only if the provider officially supports POST authentication. Do not assume that changing the request method is compatible with the API.
-
Remove or strongly discourage
--key. Prefer environment-based secret injection or a dedicated credential file with restrictive permissions. If command-line support must remain, display an explicit warning that the value may be stored in shell history and process telemetry. -
When
.envis used, verify that it is excluded from version control and recommend owner-only permissions such as mode0600on systems that support POSIX permissions. -
Remove the unused encoded credential copy:
python data = urllib.parse.urlencode(params).encode("utf-8") -
Ensure exception messages and diagnostics never print complete request URLs. Apply centralized redaction for parameter names such as
key,token, andauthorization. -
Rotate any API key suspected of having appeared in command history, telemetry, or URL logs, and review the provider's usage records for unexpected requests.
-
