T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:25
- Finding
- Zerion API Key Exposed Through Chat and Third-Party Model Prompts## Vulnerability Details **File Location**: `SKILL.md`, lines 25-26, 45-60, and 171-186 **Vulnerability Type**: Sensitive credential exposure across unnecessary trust boundaries **Risk Level**: High ### Vulnerable Code The Skill directs the user to submit the API key through ordinary chat and retain it in conversational memory: ```markdown 1. **At the start of any Zerion-related task**, if no API key has been provided yet, ask: *"To query Zerion, I'll need your API key. You can find it at https://dashboard.zerion.io/. Please paste it here."* 2. **Store the key in memory** for the duration of the conversation. Never write it to files, display it in artifacts, or log it. 3. **Pass the key** to the MCP server or REST calls as described below. ``` It then instructs generated artifacts to interpolate the key into natural-language prompt content: ```markdown When building artifacts that call the Anthropic API with MCP, include the key in the inner prompt so the inner Claude can authenticate: ```javascript mcp_servers: [ { type: "url", url: "https://developers.zerion.io/mcp", name: "zerion-mcp" } ] ``` **Important**: In artifacts, receive the API key as a prop or state variable — never hardcode it. Example pattern: ```jsx // User inputs key via a secure input field (type="password") const [apiKey, setApiKey] = useState(""); // Pass key to inner Claude prompt so MCP calls authenticate const prompt = `Using the Zerion API key: ${apiKey}, get portfolio for wallet 0x...`; ``` ``` A later example explicitly sends the prompt containing the credential to the Anthropic API: ```javascript const response = await fetch("https://api.anthropic.com/v1/messages", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "claude-sonnet-4-20250514", max_tokens: 1000, messages: [ { role: "user", content: `Use the Ze ...[truncated 3510 chars]
- Remediation
- ## Remediation Suggestions 1. **Do not request reusable credentials in ordinary chat.** Collect the key through a platform-provided secret input or credential-management interface that excludes it from conversation history. 2. **Never place the key in model prompt content.** Remove all examples that interpolate `${apiKey}` into `messages[].content`. 3. **Use a dedicated authentication channel.** Supply the key directly to Zerion through the documented HTTP `Authorization` header or a trusted MCP secret/header configuration that does not expose it to the model. 4. **Move API access to a controlled backend.** Generated browser artifacts should call a narrowly scoped server-side proxy rather than handling reusable Zerion credentials in React state. 5. **Enforce destination restrictions.** Ensure the credential can only be transmitted to approved Zerion endpoints over TLS and cannot be forwarded to arbitrary MCP servers, model providers, URLs, or tools. 6. **Apply secret redaction.** Configure application, proxy, telemetry, and error logging to redact authorization headers and values matching Zerion key formats. 7. **Minimize credential lifetime and scope.** Prefer short-lived, least-privileged tokens where supported, and clear ephemeral credential state immediately after the request. 8. **Rotate exposed credentials.** Users who followed the documented workflow should revoke or rotate their Zerion keys and review usage for unauthorized activity. 9. **Update security guidance and examples.** Replace the vulnerable snippets with backend or MCP secret-injection examples, and explicitly prohibit transmitting credentials as natural-language content.
