T09 · Insecure Skill Coding Practices
Error
- Location
- tools/katbot_client.py:247
- Finding
- Hyperliquid Agent Private Key Is Transmitted to Excessive and Configurable API Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `tools/katbot_client.py:247-258`, with affected calls throughout `tools/katbot_client.py` **Vulnerability Type**: Excessive credential transmission and failure to enforce least privilege **Risk Level**: Critical ### Vulnerable Code ```python def _auth(token: str, agent_key: str = None) -> dict: """Build auth headers with optional agent private key. CRITICAL: ALWAYS include X-Agent-Private-Key for Hyperliquid portfolio calls. The API requires this header for all Hyperliquid portfolio endpoints. """ headers = {"Authorization": f"Bearer {token}"} # Always include agent key if available - required for Hyperliquid portfolios if agent_key: headers["X-Agent-Private-Key"] = agent_key elif AGENT_PRIVATE_KEY: headers["X-Agent-Private-Key"] = AGENT_PRIVATE_KEY return headers ``` The helper is then used for operations that do not require a trading key, including: ```python def list_portfolios(token: str) -> list: """List all portfolios for the authenticated user.""" r = requests.get(f"{BASE_URL}/portfolio", headers=_auth(token)) r.raise_for_status() return r.json() ``` ```python def list_agents(token: str) -> list: """List all agents owned by the authenticated user.""" r = requests.get(f"{BASE_URL}/agents", headers=_auth(token)) r.raise_for_status() return r.json() ``` ```python def get_user(token: str) -> dict: """Get current authenticated user details and subscription info.""" r = requests.get(f"{BASE_URL}/user", headers=_auth(token)) r.raise_for_status() return r.json() ``` Market-intelligence calls use the same helper: ```python r = requests.get(f"{BASE_URL}/market-intelligence/trending", params=params, headers=_auth(token)) ``` The destination is configurable: ```python BASE_URL = os.getenv("KATBOT_BASE_URL") ... if not BASE_URL: BASE_URL = os.getenv("KATBOT_BASE_URL", "https://api.katb ...[truncated 2060 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Make the default authentication helper return only the bearer token: ```python def _auth(token: str) -> dict: return {"Authorization": f"Bearer {token}"} ``` 2. Introduce a separate helper for the small set of operations that genuinely require the trading key: ```python def _trading_auth(token: str, agent_key: str) -> dict: return { "Authorization": f"Bearer {token}", "X-Agent-Private-Key": agent_key, } ``` 3. Require each credential-bearing call site to explicitly request the trading key. 4. Do not send the key to market-intelligence, user, plan, agent-management, research-listing, polling, or other read-only endpoints. 5. Restrict credential-bearing requests to an allowlist containing the expected HTTPS origin. 6. Reject plaintext HTTP base URLs. 7. Disable cross-origin redirects for credential-bearing calls or verify every redirect target before following it. 8. Present user consent at the point where the key will actually be transmitted, rather than relying only on documentation. 9. Prefer local transaction signing so the remote API never receives the private key. ]]>
