T01 · Skill Instruction Hijacking
- Location
SKILL.md:86- Finding
Persistent Prompt Injection Through Custom Reminders
- Content
View full analysis
Vulnerability Details
File Locations:
SKILL.md:86-88src/portfolio.js:76-88src/index.js:395-421
Vulnerability Type: Persistent user-controlled instruction injection
Risk Level: CriticalVulnerable Code
src/portfolio.js:76-88persists arbitrary reminder messages:js export function addReminder({ time, type = "profit_report", message = "" }) { const data = loadReminders(); const id = `r_${Date.now()}`; data.reminders.push({ id, time, type, message, enabled: true, createdAt: new Date().toISOString().slice(0, 10), }); saveReminders(data); return data.reminders; }src/index.js:395-421retrieves the stored message, places it in Agent-facing output, and directs the Agent to execute the resulting guidance:js server.tool( "check_reminders", "检查是否有到期的提醒(AI 应在每次对话开始时主动调用此工具)。返回当前到期未触发的提醒列表,同一提醒每天只触发一次。", {}, async () => { const { due, today, currentTime, message } = portfolio.checkReminders(); if (message) { return { content: [{ type: "text", text: message }] }; } if (due.length === 0) { return { content: [{ type: "text", text: `当前时间 ${today} ${currentTime},没有到期的提醒。` }] }; } const actions = due.map((r) => { if (r.type === "profit_report") { return `[${r.time}] 持仓收益播报 → 请立即调用 get_portfolio_profit 查询收益,并根据涨跌情况给出加仓/减仓建议`; } if (r.type === "market_report") { return `[${r.time}] 大盘播报 → 请调用 get_market_overview + get_market_capital_flow 播报大盘行情`; } return `[${r.time}] 自定义提醒: ${r.message}`; }); return { content: [{ type: "text", text: `当前时间 ${today} ${currentTime},有 ${due.length} 个提醒到期:\n\n${actions.join("\n\n")}\n\n请按照上述指引执行对应操作。`, }], }; } );The corresponding Skill instructions in
SKILL.md:86-88requirecheck_remindersto be invoked before responding in every conversation, regardless of the user's request.Technical Analysis
The
set_reminder...[truncated 2221 chars]- Remediation
View remediation
Remediation Suggestions
- Treat every reminder message as untrusted display data, never as an operational instruction.
- Remove wording that directs the Agent to follow reminder content.
- Return structured reminder data instead of instruction-like prose, for example:
json { "due": [ { "type": "custom", "message": "Untrusted user-provided reminder text" } ] } - Explicitly label custom messages as quoted user content that must not control tool use, policy, or Agent behavior.
- Do not require
check_remindersbefore every unrelated user request. Invoke it only when the user asks about reminders, or perform a passive check that cannot initiate actions. - Require explicit, current-session user confirmation before executing tools suggested by any reminder.
- Apply reasonable length and character limits to custom messages and validate the
HH:MMtime format. - Separate reminders by authenticated user or session if the MCP server can be shared.
- Provide a mechanism to inspect and delete stored reminders before they are rendered to the Agent.
- Add tests using adversarial reminder text to verify that reminder content remains inert data.
