T09 · Insecure Skill Coding Practices
Error
- Location
- main.py:10
- Finding
- Hardcoded SkillPay API Credential Can Be Disclosed Through a Configurable Network Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `main.py:10-12`, `main.py:194-199`, and `main.py:217-221` **Vulnerability Type**: Hardcoded secret and credential exfiltration through an attacker-controlled endpoint **Risk Level**: High ### Vulnerable Code ```python SKILLPAY_API_KEY = "sk_8b36c2ca9e774eb0243752f907b086e78c8af866a4088d3e3475113ed446b71" SKILLPAY_API_BASE = os.getenv("SKILLPAY_API_BASE", "https://api.skillpay.me") SKILLPAY_WEB_BASE = os.getenv("SKILLPAY_WEB_BASE", "https://pay.skillpay.me") ``` The credential is transmitted when a charge is created: ```python def create_skillpay_charge(amount: str, currency: str) -> Tuple[str, str]: if not SKILLPAY_API_KEY: raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY") url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges" headers = { "Authorization": f"Bearer {SKILLPAY_API_KEY}", "Content-Type": "application/json", } body = { "amount": amount, "currency": currency, "title": "OpenClaw Skill Payment", "description": "Polymarket Movers x3", } r = requests.post(url, json=body, headers=headers, timeout=20) if r.status_code not in (200, 201): raise HTTPException(status_code=502, detail="SkillPay create charge failed") data = r.json() cid = str(data.get("id") or data.get("charge_id") or "") purl = data.get("payment_url") if not purl and cid: purl = f"{SKILLPAY_WEB_BASE.rstrip('/')}/checkout/{cid}" if not cid or not purl: raise HTTPException(status_code=502, detail="Invalid SkillPay response") return cid, purl ``` It is also transmitted during payment-status queries: ```python def get_skillpay_status(charge_id: str) -> str: if not SKILLPAY_API_KEY: raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY") url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges/{charge_id}" headers = {"Authorization": f"Bearer {SKILLPAY_API_KE ...[truncated 3219 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Immediately revoke and rotate the exposed SkillPay API credential. Treat it as compromised even if the repository was not intended to be public. 2. Remove the credential from source code, repository history, build artifacts, container layers, logs, and distributed packages. 3. Load the key from the required environment variable or a managed secret store: ```python SKILLPAY_API_KEY = os.getenv("SKILLPAY_API_KEY") if not SKILLPAY_API_KEY: raise RuntimeError("SKILLPAY_API_KEY is required") ``` 4. In production, use a fixed SkillPay API origin rather than accepting an arbitrary base URL from the environment. 5. If endpoint configurability is required for controlled testing, validate the parsed URL before sending credentials: - Require HTTPS. - Require an exact approved hostname. - Reject embedded user information, unexpected ports, IP literals, redirects to unapproved hosts, and hostname suffix tricks. 6. Disable automatic redirects for credential-bearing requests or verify every redirect destination before forwarding the Authorization header. 7. Give the replacement credential only the minimum provider-side permissions needed to create and verify charges. 8. Use separate credentials for development, testing, and production, and configure provider-side restrictions where available. 9. Add automated secret scanning to source-control and CI pipelines so commits containing API-key patterns are rejected. 10. Add tests confirming that credential-bearing requests can only be sent to approved SkillPay endpoints. ]]>
