T09 · Insecure Skill Coding Practices
Warning
- Location
- renderer.js:50
- Finding
- Untrusted candidate data is rendered without Markdown escaping< | ${item.permissions.join("、")} | ${item.recommendation} | \`${item.installCommand}\` |`); return [header, ...rows].join("\n"); ``` ### Technical Analysis The Markdown renderer directly interpolates externally sourced candidate fields into a Markdown table. Fields such as `name`, `description`, `sourceUrl`, `permissions`, and `installCommand` are not escaped or structurally validated before rendering. An attacker-controlled candidate can include Markdown metacharacters such as pipes, newlines, brackets, parentheses, or backticks. These characters can terminate table cells or inline-code spans, create additional rows, introduce deceptive links, or otherwise alter the apparent relationship between a candidate and its installation command. The normalization performed by `normalizeCandidate()` only supplies defaults and checks a small number of enumerations. It does not neutralize Markdown syntax or restrict `sourceUrl` to HTTP or HTTPS. ### Attack Path 1. An attacker publishes a Skill listing with crafted metadata in a marketplace or repository searched by the agent. 2. The candidate metadata contains Markdown control characters, such as a newline and pipe sequence in the description or backticks in the installation command. 3. The agent retrieves the listing and constructs a candidate object from that data. 4. `renderCandidates()` interpolates the fields directly into the Markdown table. 5. The rendered table is structurally altered, allowing the attacker to display deceptive candidate details, ...[truncated 965 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Implement context-aware Markdown escaping for every externally sourced field. - Replace embedded carriage returns and newlines with spaces before rendering table cells. - Escape table separators, brackets, parentheses, backticks, backslashes, and other Markdown control characters. - Render installation commands using a robust fenced-code strategy or escape backticks based on the longest backtick sequence in the value. - Parse `sourceUrl` with `new URL()` and allow only explicitly approved protocols, preferably `https:` and, where necessary, `http:`. - Reject malformed URLs and render them as plain text rather than links. - Apply reasonable length limits to externally sourced fields. - Add tests containing pipes, newlines, brackets, parentheses, backticks, embedded links, and malicious URL schemes. - Treat rendering sanitization as a mandatory enforcement layer rather than relying only on instructions in `SKILL.md`. ]]>
