T09 · Insecure Skill Coding Practices
- Location
scripts/inbox.js:192- Finding
Unescaped Markdown Allows Operator-Facing Content Injection
- Content
View full analysis
Vulnerability Details
File Location:
scripts/inbox.js, lines 192-208
Vulnerability Type: Markdown output injection
Risk Level: MediumVulnerable Code
javascript function renderMD(msgs, unread, total, filter) { const filterLabel = filter === 'unread' ? 'Unread' : filter === 'archived' ? 'Archived' : 'All'; if (msgs.length === 0) { return `**📬 Inbox** *(${filterLabel})*\n✅ ${filter === 'unread' ? 'All caught up!' : 'No messages.'}`; } let md = `**📬 Inbox** — *${unread} unread · ${total} total · ${filterLabel}*\n`; for (const m of msgs.slice(0, 15)) { const prioIcon = m.priority === 'urgent' ? '🔴 ' : m.priority === 'important' ? '🟡 ' : ''; const prioTag = m.priority !== 'normal' ? ` **[${m.priority.toUpperCase()}]**` : ''; const bullet = m.read ? ' ✓' : ' •'; const date = fmtDate(m.createdAt); md += `\n${bullet} ${prioIcon}**${m.title}**${prioTag}\n`; md += ` ${m.message}\n`; md += ` \`${date}\` · \`${m.id}\`\n`; } if (msgs.length > 15) md += `\n*…and ${msgs.length - 15} more*`; return md; }Technical Analysis
The Markdown renderer interpolates the stored
m.titleandm.messagefields directly into output without escaping Markdown metacharacters or neutralizing platform mention syntax. These fields originate from the arguments accepted by theaddcommand and can therefore contain links, formatting delimiters, code fences, headings, or mention expressions.The HTML renderer applies the
esc()function to the same fields, but the Markdown renderer has no equivalent protection.SKILL.mdinstructs the agent to send rendered output directly when presenting the inbox, so malicious formatting can reach Discord or Slack without an additional sanitization boundary.Exploitation requires an attacker to influence content that the agent places in an inbox entry, or otherwise obtain write access to the inbox data. The vuln ...[truncated 1336 chars]
- Remediation
View remediation
Remediation Suggestions
- Add a dedicated Markdown-escaping function and apply it to every untrusted field, particularly
m.titleandm.message, before interpolation. - Escape or neutralize Markdown control characters such as backslashes, asterisks, underscores, backticks, brackets, parentheses, angle brackets, pipes, tildes, and heading prefixes according to the target platform.
- Neutralize platform mention syntax such as
@everyone,@here, user mentions, and role mentions. Where available, use the messaging API's option to disable mention parsing. - Consider separate renderers for Discord and Slack because their formatting and mention semantics differ.
- Validate and constrain the maximum title and message lengths to reduce formatting abuse and output flooding.
- Add regression tests covering deceptive links, code fences, headings, spoilers, nested formatting, backslashes, mass mentions, user or role mentions, and multiline content.
- Preserve HTML escaping in
renderHTML()and document that all output renderers must treat stored inbox values as untrusted data.
- Add a dedicated Markdown-escaping function and apply it to every untrusted field, particularly
