T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate_weekly_report.py:26
- Finding
- Client Secret Transmitted in URL Query Parameters## Vulnerability Details **File Locations**: - `scripts/generate_weekly_report.py:26-34` - `scripts/get_my_tasks.py:25-33` - `scripts/get_projects.py:25-33` - `scripts/get_project_workitems.py:25-33` - `scripts/update_workitem.py:26-34` - `references/api_docs.md:7-9` **Vulnerability Type**: Sensitive credential exposure through URL query parameters **Risk Level**: Medium ### Vulnerable Code The same authentication pattern is used by all five scripts: ```python url = f"{BASE_URL}/v1/auth/token" params = { "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET } try: response = requests.get(url, params=params, timeout=30) ``` The accompanying API documentation explicitly recommends the same pattern: ```http GET https://open.pingcode.com/v1/auth/token?grant_type=client_credentials&client_id={client_id}&client_secret={client_secret} ``` ### Technical Analysis Passing `client_secret` through `requests.get(..., params=params)` serializes the secret into the request URL. TLS protects the request while it is in transit, but it does not prevent the complete URL from being recorded at endpoints or infrastructure that processes the request. Query strings may be captured by HTTP client diagnostics, reverse-proxy access logs, application performance monitoring systems, exception telemetry, network debugging tools, or server-side request logs. Unlike an authorization header, query parameters are commonly treated as ordinary request metadata and may not be redacted automatically. The scripts avoid printing `response.text`, but that precaution does not address exposure of the outbound request URL. ### Attack Path 1. An operator configures a valid `PINGCODE_CLIENT_ID` and `PINGCODE_CLIENT_SECRET`. 2. The operator executes any of the five scripts. 3. The script issues a GET request whose URL contains both credentials. 4. A proxy, telemetry age ...[truncated 1011 chars]
- Remediation
- ## Remediation Suggestions 1. Use PingCode's supported POST-based token exchange, placing credentials in the request body or an authorization header rather than the URL: ```python response = requests.post( f"{BASE_URL}/v1/auth/token", data={ "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, }, timeout=30, ) ``` 2. If PingCode supports HTTP Basic client authentication, prefer `auth=(CLIENT_ID, CLIENT_SECRET)` and omit the credentials from the URL and request body. 3. Confirm the exact supported authentication method against the current official PingCode documentation before deployment. 4. Apply the corrected authentication implementation consistently to all five scripts, preferably through one shared API client module. 5. Update `references/api_docs.md` so it no longer instructs users to place secrets in query strings. 6. Configure HTTP, proxy, and telemetry logging to redact `client_id`, `client_secret`, access tokens, and `Authorization` headers. 7. Rotate any client secret that may already have appeared in logs and review access logs for unauthorized token requests. 8. Grant the application only the minimum PingCode scopes required by these scripts.
