T09 · Insecure Skill Coding Practices
- Location
- SKILL.md:301
- Finding
- Untrusted MCP Card Content Is Injected into Model Conversations<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 301–323 **Vulnerability Type**: Indirect prompt injection through card-to-model communication **Risk Level**: High ### Vulnerable Code Snippet ```typescript // The specification states that each update replaces the previous // snapshot for the same view. stageModelContext(viewId, extractTextContent(params)) // When the next user message is sent: buildOutgoingUserText(userText) // → "Card state:\nSearch: Bluetooth headphones Price<100\n\nUser-entered text" ``` ```typescript requestMcpAppUserMessage(text) // → message channel → submitText submission path ``` The documented security control is limited to per-card debounce: ```typescript requestMcpAppUserMessage(text, debounceKey) ``` ### Technical Analysis The Skill instructs host developers to accept text originating from remotely supplied MCP card HTML and either: 1. Silently prepend it to the next user message through `ui/update-model-context`; or 2. Submit it as a new conversation turn through `ui/message`. The MCP server controls the card HTML and scripts. Consequently, the text passed to these APIs must be treated as untrusted remote input. The described implementation does not require user confirmation, display the injected context to the user, enforce a content schema, distinguish data from instructions, or prevent card content from being interpreted as agent instructions. Per-card debounce only limits request frequency. It does not prevent a single malicious message from influencing the model or causing the agent to invoke tools. ### Attack Path 1. A user connects to or invokes a malicious or compromised MCP server. 2. The server returns MCP App HTML containing attacker-controlled JavaScript. 3. The card sends crafted text through `ui/update-model-context`, such as instructions to ignore the user's request or disclose available context. 4. The host stores the text and invisibly prepends it to the user's next message. 5. A ...[truncated 845 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat all card-originated text as untrusted structured data rather than user instructions. 2. Do not silently concatenate card content with user-authored messages. 3. Display the proposed card message and require explicit user approval before starting a model turn. 4. Mark card data with immutable provenance metadata, including server identity, card identity, and tool-call ID. 5. Pass view state through a dedicated structured field instead of embedding it in natural-language prompt text. 6. Enforce strict schemas, maximum lengths, accepted character/content types, and per-card quotas. 7. Ensure card-originated content cannot authorize tools or override system, developer, or user instructions. 8. Apply tool-specific confirmation and authorization independently of model output. 9. Retain rate limiting, but supplement debounce with total request quotas and cancellation controls. 10. Add adversarial tests covering instruction-like card state, repeated `ui/message` calls, and attempted tool authorization. ]]>
