T09 · Insecure Skill Coding Practices
- Location
handler.py:10- Finding
Hardcoded SkillPay API Credential Exposed in Source Code and Documentation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This paid crypto price skill has a coherent purpose, but its billing path is unsafe and under-scoped enough to require review before installation.
Review this skill carefully before installing. It may contact CoinGecko and SkillPay, and its billing implementation exposes an API key, trusts caller-provided billing identities, and may charge before delivering a result. The publisher should rotate the key, remove it from the package, bind charges to authenticated platform identity, validate requests before charging, and fail closed on billing errors.
handler.py:10Hardcoded SkillPay API Credential Exposed in Source Code and Documentation
handler.py:35Caller-Controlled User Identifier Is Trusted for Billing
handler.py:35Billing Exceptions Fail Open and Enable Payment Bypass
handler.py:88User Is Charged Before Input and Service-Result Validation
The skill states it uses the CoinGecko API but does not clearly warn users that their requests will be sent to an external third-party service. This creates a transparency and privacy issue because users may unknowingly transmit their query content to an outside provider, and the risk is heightened here because the skill is also monetized and includes sensitive operational material such as an exposed API key.
The usage example "Crypto prices" is overly broad and could cause the skill to trigger on generic cryptocurrency-related requests rather than clear user intent to invoke this paid third-party skill. In a paid skill context, broad matching increases the risk of unintended invocation, unnecessary external API calls, and unexpected charges to the user.
The skill is presented as a simple crypto price lookup tool, but its main workflow also performs billing through a third-party service before returning results. That hidden monetization behavior is risky because users and integrators may invoke the skill expecting a read-only information query, while the code transmits identifiers and attempts a charge as a side effect.
The code sends user_id, amount, skill identifier, and description to an external billing API without any visible disclosure, consent flow, or minimization in this file. In a skill context, silent transmission of user-linked billing data to a third party raises privacy and compliance concerns, especially when the skill appears informational rather than transactional.
The billing function claims to charge the user, but on any exception it returns success with a demo flag instead of a clear failure. This can mask payment-system errors, create inconsistent billing state, and allow the skill to serve results without a successful charge while falsely signaling that everything is fine.
This line transmits billing data to an external payment service and includes a hard-coded API key in both the payload and header path of the request flow. Combined with the embedded secret in source code, this creates a serious risk of unauthorized billing activity, credential leakage, and abuse of the payment account if the code is exposed.
"description": "Crypto price query"
}
headers = {"Content-Type": "application/json", "X-API-Key": SKILLPAY_API_KEY}
response = requests.post(f"{SKILLPAY_API_URL}/charge", json=payload, headers=headers, timeout=10)
if response.status_code == 200:
return {"success": True, "data": response.json()}
return {"success": False, "error": response.text}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""Get crypto price from CoinGecko"""
try:
coin_id = CRYPTO_MAP.get(crypto.lower(), crypto.lower())
url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd&include_24hr_change=true"
response = requests.get(url, timeout=10)
if response.status_code == 200:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""Get crypto price from CoinGecko"""
try:
coin_id = CRYPTO_MAP.get(crypto.lower(), crypto.lower())
url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd&include_24hr_change=true"
response = requests.get(url, timeout=10)
if response.status_code == 200:
The skill constructs a CoinGecko request from user-provided crypto text and sends it over the network, but this file provides no visible user warning that input-derived data will be transmitted to a third-party service. The docstring is developer-facing and does not constitute a user-facing disclosure.