T09 · Insecure Skill Coding Practices
- Location
SKILL.md:25- Finding
Zerion API Key Exposed Through Chat and Third-Party Model Prompts
- Content
View full analysis
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: HighVulnerable 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...`;text 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
View remediation
Remediation Suggestions
- 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.
- Never place the key in model prompt content. Remove all examples that interpolate
${apiKey}intomessages[].content. - Use a dedicated authentication channel. Supply the key directly to Zerion through the documented HTTP
Authorizationheader or a trusted MCP secret/header configuration that does not expose it to the model. - 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.
- 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.
- Apply secret redaction. Configure application, proxy, telemetry, and error logging to redact authorization headers and values matching Zerion key formats.
- Minimize credential lifetime and scope. Prefer short-lived, least-privileged tokens where supported, and clear ephemeral credential state immediately after the request.
- Rotate exposed credentials. Users who followed the documented workflow should revoke or rotate their Zerion keys and review usage for unauthorized activity.
- 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.
