T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/search-stock.mjs:91
- Finding
- Untrusted Search Content Is Forwarded to the Agent Without Prompt-Injection Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search-stock.mjs:91-113`; related consumption instructions in `SKILL.md:66-69` **Vulnerability Type**: Indirect prompt injection through untrusted remote search content **Risk Level**: Medium ### Vulnerable Code ```javascript // Print AI-generated answer if available if (data.answer) { console.log("## Answer\n"); console.log(data.answer); console.log("\n---\n"); } // Print results const results = (data.results ?? []).slice(0, n); console.log("## Sources\n"); for (const r of results) { const title = String(r?.title ?? "").trim(); const url = String(r?.url ?? "").trim(); const content = String(r?.content ?? "").trim(); const score = r?.score ? ` (relevance: ${(r.score * 100).toFixed(0)}%)` : ""; if (!title || !url) continue; console.log(`- **${title}**${score}`); console.log(` ${url}`); if (content) { console.log(` ${content.slice(0, 300)}${content.length > 300 ? "..." : ""}`); } console.log(); } ``` The related workflow in `SKILL.md:66-69` directs the agent to consume this output: ```markdown 用户说 "今天A股怎么样" 或 "帮我分析一下股市" 时: 1. 运行搜索脚本获取最新数据 2. 综合分析给出投资建议 ``` ### Technical Analysis The script retrieves an AI-generated answer and search-result fields from Tavily, then writes those fields directly to standard output. The returned `data.answer`, result titles, URLs, and content excerpts originate from external services and indexed websites. They are therefore attacker-influenced data. No trust-boundary marker, output encoding policy, prompt-injection filtering, source allowlist, or instruction hierarchy is applied before this content is exposed to the agent. Meanwhile, the Skill instructs the agent to synthesize the script output into investment analysis. An indexed page can consequently place instruction-like text in its title or content, and Tavily may reproduce that text in `data.answer` or `results[].content`. This does not cause the Node.js script itself to ...[truncated 1923 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Update `SKILL.md` to state explicitly that all search answers, titles, URLs, and excerpts are untrusted evidence and must never override system, developer, user, or Skill instructions. 2. Require the agent to ignore commands, requests, policies, or tool instructions embedded in search results. 3. Return structured JSON rather than instruction-like Markdown. Place remote values only in clearly named data fields such as `source_title`, `source_url`, and `source_excerpt`. 4. Disable `include_answer` by default or avoid forwarding `data.answer`, because it is generated from potentially adversarial source material and can blend source data with instruction-like prose. 5. Validate returned URLs and consider an allowlist of reputable financial and regulatory sources. 6. Require corroboration from multiple independent, trusted sources before producing factual market claims or investment recommendations. 7. Preserve source attribution so the agent and user can distinguish reported facts from generated summaries. 8. Apply output length limits and normalization to every remote field. Filtering alone must not be treated as a complete prompt-injection defense. 9. Ensure the host agent uses least privilege and requires confirmation before consequential tool actions. 10. Add adversarial tests containing instruction-like text in search titles, excerpts, and generated answers to verify that such text remains treated solely as untrusted data. ]]>
