T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:44
- Finding
- Bearer API Key Can Be Sent to an Untrusted Configurable Endpoint## Vulnerability Details **File Location**: `SKILL.md:8-9, 44-45, 56-89, 97-110` **Vulnerability Type**: Unrestricted credential destination **Risk Level**: Medium The Skill defines the API destination through an environment variable and then sends the Zen+ Health bearer credential to that destination without requiring validation of its scheme, host, port, or embedded credentials. ```yaml base_url_env: ZEN_API_BASE_URL api_key_env: ZEN_API_KEY ``` The vulnerable pattern appears in all documented API commands, including: ```bash curl -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/me/notifications?limit=10" ``` ```bash curl -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/me/timeline?days=7" ``` ```bash curl -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/catalog" ``` ```bash curl -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/me" ``` The response-processing examples repeat the same behavior: ```bash # Get notification titles curl -s -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/me/notifications" | jq -r '.notifications[].title' # Count timeline events curl -s -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/me/timeline" | jq '.events | length' # List catalogue categories curl -s -H "Authorization: Bearer ${ZEN_API_KEY}" \ "${ZEN_API_BASE_URL}/v1/catalog" | jq -r '.tasks[].category' | sort -u ``` ### Technical Analysis `ZEN_API_BASE_URL` is externally configurable and is interpolated directly into each `curl` destination. The Skill does not require the agent or a wrapper to verify that the parsed URL: - Uses HTTPS. - Has the exact approved hostname `api.zenplus.health`. - Uses an approved port. - Contains no embedded credentials or malformed URL components. As a result, configuration tampering or setup error ...[truncated 1828 chars]
- Remediation
- ## Remediation Suggestions 1. Hardcode `https://api.zenplus.health` as the API origin if alternative environments are not strictly required. 2. If configurability is necessary, place all requests behind a wrapper that parses and validates the URL before adding the authorization header. 3. Require: - The `https` scheme. - An exact hostname allowlist, such as `api.zenplus.health`. - An approved port, normally `443`. - No username or password component. - No ambiguous or malformed hostname encoding. 4. Fail closed if validation fails; do not fall back to the supplied URL. 5. Perform structured URL parsing rather than prefix or substring checks. For example, a value such as `https://api.zenplus.health.attacker.example` must not pass validation. 6. Add the bearer header only after destination validation. 7. Prevent commands, debug traces, error handling, and API response logs from exposing the credential. 8. Use a dedicated, revocable key with only the scopes required by enabled Skill features. 9. Consider separating public catalogue access from authenticated personal-data operations so credentials are not attached where authentication is unnecessary. 10. Update `SECURITY.md` to distinguish recommended configuration from enforced controls and document the destination-validation requirement.
