Back to skill

Security audit

Snail Mail

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local operator inbox that stores and renders messages, with some formatting and trigger-scope caveats but no evidence of hidden access, exfiltration, or destructive behavior.

Install only if you want an agent-managed local inbox. Set OPENCLAW_WORKSPACE to the intended workspace so messages are not stored under $HOME by fallback, and treat inbox content as untrusted if entries may include externally supplied text, especially when rendering to Discord or Slack.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
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: Medium

Vulnerable 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.title and m.message fields directly into output without escaping Markdown metacharacters or neutralizing platform mention syntax. These fields originate from the arguments accepted by the add command 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.md instructs 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

  1. Add a dedicated Markdown-escaping function and apply it to every untrusted field, particularly m.title and m.message, before interpolation.
  2. 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.
  3. 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.
  4. Consider separate renderers for Discord and Slack because their formatting and mention semantics differ.
  5. Validate and constrain the maximum title and message lengths to reduce formatting abuse and output flooding.
  6. Add regression tests covering deceptive links, code fences, headings, spoilers, nested formatting, backslashes, mass mentions, user or role mentions, and multiline content.
  7. Preserve HTML escaping in renderHTML() and document that all output renderers must treat stored inbox values as untrusted data.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The activation examples for showing the inbox include generic terms like "inbox" and especially "messages," which overlap with common everyday speech and could cause unintended invocation. Although some examples are specific, the trigger scope is not constrained with negative examples or context boundaries.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.