Back to skill

Security audit

RSS Summarizer

Security checks for vulnerabilities and agentic risk

Overview

This RSS skill mostly does what it says, but it fetches arbitrary user-supplied feed URLs without network-safety limits and passes untrusted feed text back to the agent.

Review this skill before installing in environments with access to private networks, localhost services, cloud metadata endpoints, or sensitive agent tools. Use only trusted feed URLs, treat all feed content as untrusted data, and prefer adding URL validation and output isolation before using it broadly.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/_lib.js:89
Finding
Server-Side Request Forgery Through Unrestricted RSS Feed URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/add.js:11`, `scripts/_lib.js:56-69`, and `scripts/_lib.js:89-103` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code `scripts/add.js:11`: ```js const result = await addSubscription(input.url, input.name); ``` `scripts/_lib.js:56-69`: ```js export async function addSubscription(url, name) { const subs = loadSubs(); if (subs.some(s => s.url === url)) { return { success: false, error: '该订阅源已存在' }; } const newSub = { id: generateId(), url, name: name || url, addedAt: new Date().toISOString(), lastFetchedAt: null }; subs.push(newSub); saveSubs(subs); return { success: true, message: '订阅源已添加', subscription: newSub }; } ``` `scripts/_lib.js:89-103`: ```js export async function fetchSubscriptions(targetId = null, format = 'markdown', notify = false, sendFn = null) { const subs = loadSubs(); const targets = targetId ? subs.filter(s => s.id === targetId) : subs; if (targets.length === 0) { return { success: false, error: '没有找到订阅源' }; } const config = loadConfig(); const parser = new Parser(); const results = []; for (const sub of targets) { try { const feed = await parser.parseURL(sub.url); ``` ### Technical Analysis The subscription URL comes directly from JSON input and is stored without validating its scheme, hostname, resolved address, port, or redirect destination. During a subsequent fetch, `rss-parser` passes that stored value to its HTTP retrieval mechanism. This creates an SSRF primitive because the requester can cause the Skill runtime to initiate connections to arbitrary destinations reachable from its network context. Potential targets include: - Loopback services such as `127.0.0.1` or `::1`. - Private network ranges. - Link-local addresses. - Cloud instance metadata services such as `169.254.169.254`. - Internal administrative applications or APIs unavailable to th ...[truncated 1552 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every submitted URL using the platform URL parser and reject malformed values. 2. Allow only explicitly required schemes, preferably `https:`. Reject local-file and non-HTTP protocols. 3. Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, unspecified, and reserved ranges for both IPv4 and IPv6. 4. Re-resolve and revalidate the destination at connection time to reduce DNS rebinding exposure. 5. Disable redirects or validate the scheme, hostname, and resolved address after every redirect. 6. Explicitly block cloud metadata destinations and hostnames. 7. Apply strict connection, read, and total request timeouts. 8. Enforce a maximum response size and XML complexity limits. 9. Use network-level egress controls so the process cannot access metadata services or internal administrative networks. 10. Perform the same validation when loading existing subscriptions, because the JSON data file could contain entries created before validation was introduced. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
scripts/_lib.js:107
Finding
Indirect Prompt Injection Through Untrusted RSS Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_lib.js:107-119` and `scripts/_lib.js:157-165` **Vulnerability Type**: Indirect prompt injection through untrusted remote content **Risk Level**: Medium ### Vulnerable Code `scripts/_lib.js:107-119`: ```js results.push({ subscription: sub.name, feedTitle: feed.title, items: items.map(item => ({ title: item.title, link: item.link, pubDate: item.pubDate, contentSnippet: item.contentSnippet })) }); sub.lastFetchedAt = new Date().toISOString(); saveSubs(subs); ``` `scripts/_lib.js:157-165`: ```js } else { // markdown const text = results.map(r => `## ${r.subscription}${r.feedTitle ? ` - ${r.feedTitle}` : ''}\n` + (r.error ? `❌ ${r.error}` : (r.items || []).map(i => `- [${i.title}](${i.link})`).join('\n') ) ).join('\n\n'); output = { text }; ``` The integration instructions in `SKILL.md:25-26` direct the AI to invoke these scripts and return their results, placing the untrusted output in the Agent's processing path. ### Technical Analysis RSS feed titles, entry titles, snippets, and links are controlled by remote feed publishers. The implementation returns these fields unchanged in JSON and interpolates titles and links directly into Markdown. There is no explicit trust boundary separating remote feed data from Agent instructions. The Skill documentation also does not direct the consuming Agent to treat feed content exclusively as untrusted data. Consequently, a malicious or compromised feed can place instruction-like text in a title or snippet, such as requests to ignore prior constraints, disclose information, or invoke tools. Markdown interpolation introduces an additional presentation risk because malicious titles and link values can alter the rendered structure or present misleading destinations. Escaping output alone does not fully prevent prompt inject ...[truncated 1448 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly document that every feed-derived field is untrusted data and must never be interpreted as an instruction. 2. Return feed content in a strongly typed, clearly delimited data structure rather than mixing it with Agent-facing prose. 3. Add a fixed instruction to the consuming workflow requiring the Agent to ignore commands, policy statements, tool requests, or role changes contained in feed data. 4. Use a constrained summarization prompt that accepts only designated feed fields and prohibits tool calls or instruction following during summarization. 5. Escape Markdown metacharacters in titles and validate link schemes before rendering. 6. Reject control characters and normalize unusual Unicode that could conceal or visually spoof instructions. 7. Preserve source attribution so users can distinguish remote content from trusted Skill messages. 8. Limit the amount of remote text supplied to the Agent and avoid including unnecessary raw content. 9. Require explicit user confirmation before any subsequent privileged action suggested by feed content. 10. Apply independent authorization and policy checks to all tool calls, even if the Agent claims that a feed item requested the action. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
整体上,代码的大部分功能与“RSS 订阅、抓取、过滤”声明是一致的:它管理订阅列表,抓取 feed,应用 filters,并输出结果,还会保存配置和订阅状态。这些本地文件读写属于合理的支撑实现细节。问题在于声明明确强调“摘要”,而代码没有对文章或多条内容进行摘要处理;所谓 summaryMode 和 language 配置仅被保存,从未在抓取逻辑中使用。输出中的 contentSnippet 只是 RSS 源自带字段,不等同于该技能自行生成摘要。因此描述高估了核心能力,存在描述与实际行为不一致。

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared purpose focuses on subscribing to, fetching, filtering, and summarizing RSS/Atom feeds. The actual code chunk exclusively performs removal of a subscription by ID. Deleting subscriptions may be related to subscription management, but it is not represented in the description, and the code does not exhibit the headline behaviors described (grabbing feeds, filtering content, or generating summaries). Therefore this code chunk does not accurately match the declared description.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
Fetching RSS feeds and using an external AI service both require outbound network access, yet the description does not clearly warn users that content and possibly metadata may be sent off-host. This matters because users may unknowingly trigger requests to untrusted RSS endpoints or disclose feed contents to a third-party summarization provider.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
Defaulting output language to Chinese without explicit user opt-in is a transparency and user-intent issue that can cause misunderstandings, especially for summaries or notifications. In a security context this is lower severity, but it can still obscure what data was processed or communicated if the user expects another language.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The config sets "language": "zh", which enforces a specific language preference in a manifest/config file. Under the policy, locale or language restrictions should not be forced unless the user is given a choice or the constraint is clearly justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The defaultConfig sets `language: 'zh'`, which establishes a specific language preference by default. This is a natural-language policy concern because the file does not present this as an opt-in choice or document a justified locale restriction.

Missing User Warnings

Medium
Confidence
75% confidence
Finding
The call to addSubscription(input.url, input.name) likely performs an external subscription or network-affecting action, but this file provides no confirmation prompt, comment, docstring, or user-facing message explaining that behavior. Because the script accepts stdin input and immediately executes the action, users may not receive any visible warning at the point of use.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The skill writes subscription data and configuration to local files and may create those files automatically, but this persistence behavior is not clearly surfaced as a user warning. In agent environments, undisclosed local persistence can surprise users, leave residual data on disk, and create privacy or operational concerns if the host is shared or monitored.

Unpinned Dependencies

Low
Category
Supply Chain
Content
{
  "dependencies": {
    "rss-parser": "^3.13.0"
  }
}
Confidence
95% confidence
Finding
The dependency uses a caret range (^3.13.0), which allows automatic installation of newer minor/patch releases instead of a fully fixed version. This creates a supply-chain risk because builds may become non-reproducible and could unexpectedly pull a compromised or breaking upstream release, though the issue is limited here to a single common RSS parsing library.

Static analysis

No suspicious patterns detected.