Back to skill

Security audit

Latte News Fetcher

Security checks for vulnerabilities and agentic risk

Overview

This news skill mostly does what it claims, but it can fetch arbitrary URLs from the user’s environment without destination safeguards, which merits Review before installation.

Install only if you are comfortable with the skill making outbound news requests and sending search terms to Tavily when configured. Before using it, restrict direct fetching to trusted public news domains or add URL validation that blocks localhost, private IP ranges, metadata endpoints, unsafe schemes, and redirects to internal hosts. Treat article and webpage text as untrusted content, and review the workspace preference file behavior if you do not want the agent to persist news preferences.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetch_news.mjs:49
Finding
Unrestricted URL Fetching Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_news.mjs:14-16, 49-54, 85-96` **Vulnerability Type**: Server-Side Request Forgery (SSRF) through an unvalidated user-controlled URL **Risk Level**: High ### Complete Vulnerable Code ```js const args = process.argv.slice(2); const url = args.find(a => !a.startsWith('--')); const useDirect = args.includes('--direct'); ``` ```js async function fetchDirect(targetUrl) { const response = await fetch(targetUrl, { headers: { 'User-Agent': 'Mozilla/5.0 (compatible; NewsFetcher/2.0)' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const text = await response.text(); return { raw_content: text }; } ``` ```js console.log(`🔗 访问链接: ${url}\n`); try { const result = await fetchDirect(url); if (result.raw_content && result.raw_content.length > 300) { console.log(`✅ 成功获取`); console.log(` 📊 ${result.raw_content.length} 字符\n`); console.log('─'.repeat(50)); console.log('📄 内容:\n'); console.log(result.raw_content.substring(0, 5000)); } else { console.log('❌ 内容不足'); } ``` ### Technical Analysis The script accepts a URL directly from command-line input and passes it to `node-fetch` without validating its scheme, hostname, resolved IP address, port, or redirect destinations. Although the declared function only requires access to public news sources, the implementation permits requests to arbitrary network locations reachable from the host. Consequently, an attacker may supply URLs targeting loopback interfaces, private network ranges, link-local addresses, internal DNS names, or cloud instance metadata services. Redirects also require validation because `node-fetch` follows redirects by default; an apparently public URL could redirect to a prohibited internal endpoint. The script reads the response body and prints up to 5,000 characters. This turns otherwise blind SSRF into a response-disclosure channel and may expo ...[truncated 1572 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict accepted schemes to `https:` and permit `http:` only where explicitly required. 2. Prefer an allowlist of approved public news domains. Compare normalized hostnames exactly and prevent deceptive suffix matches. 3. Resolve destination hostnames before connecting and reject: - Loopback addresses. - RFC 1918 private addresses. - Link-local addresses. - Carrier-grade NAT ranges. - Multicast, unspecified, and reserved ranges. - IPv6 loopback, unique-local, link-local, and IPv4-mapped private addresses. 4. Disable automatic redirects or validate the scheme, hostname, and resolved address of every redirect target before following it. 5. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 6. Apply strict connection and response timeouts. 7. Stream responses with a maximum byte limit instead of loading an unlimited response into memory. 8. Validate response content types and reject unexpected binary or executable content. 9. Avoid printing raw response bodies by default. Extract and return only the news fields required by the Skill. 10. Run the fetcher in a sandbox with outbound network policy that blocks localhost, private networks, and metadata endpoints. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:65
Finding
Untrusted Web Content Is Processed Without Prompt-Injection Boundaries<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:65-75, 122-131` **Vulnerability Type**: Indirect prompt injection through untrusted webpage content **Risk Level**: Medium ### Complete Vulnerable Instruction Snippets ```text 1. browser 打开网站首页 browser action=open url="https://cn.wsj.com" profile="openclaw" 2. 等待页面加载(3-5秒) browser action=act kind=wait timeMs=5000 3. 获取页面快照 browser action=snapshot 4. 从快照中提取新闻标题和链接 5. 关闭浏览器 browser action=close ``` ```text 1. web_fetch 直接获取(免费信源) ↓ 失败 2. browser 访问页面 ↓ 失败 3. 搜索替代信源(Tavily API) ↓ 失败 4. 诚实告知 + 提供已获取的摘要 ``` ### Technical Analysis The Skill directs the Agent to open external webpages, capture their content, and extract or summarize information. It does not establish an explicit trust boundary stating that webpage text is untrusted data and must never be treated as Agent instructions. A malicious or compromised page could include prompt-like text in visible content, metadata, accessibility labels, or article bodies. When the browser snapshot or fetched content enters the Agent context, those instructions may attempt to alter the task, request additional tool calls, induce navigation to unrelated destinations, or solicit sensitive context. This issue does not establish that any listed news provider is malicious. The risk arises because the workflow can process external content, including user-selected websites, without explicit safeguards against indirect prompt injection. ### Attack Path 1. An attacker publishes or compromises a webpage that appears to contain news. 2. The page embeds instructions addressed to an AI Agent, potentially hiding them among ordinary article content. 3. A user asks the Skill to retrieve that website or article. 4. The Skill opens the page using `browser` or retrieves it using `web_fetch`. 5. The external text is included in a browser snapshot or fetched-content context. 6. Because the Skill does not explicitly require the Agen ...[truncated 981 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add an explicit instruction that all webpage, RSS, search-result, and API content is untrusted data. 2. State that instructions found in external content must never override system, developer, user, or Skill instructions. 3. Limit extraction to required fields such as article title, canonical URL, publication date, source, and article text. 4. Prohibit executing commands, revealing context, changing configuration, sending messages, or invoking unrelated tools based solely on webpage content. 5. Require user confirmation before following links or taking actions suggested by fetched content. 6. Keep retrieved content clearly delimited from trusted instructions when presenting it to the Agent. 7. Prefer deterministic parsers for RSS and structured metadata instead of placing complete raw pages into the Agent context. 8. Restrict retrieval to approved news domains where practical and warn when a user requests an unknown domain. 9. Treat text returned by Tavily and other search providers as equally untrusted. 10. Sanitize or omit hidden page elements and accessibility text not required for article extraction. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Ae1

High
Category
analysis-evasion
Content
node scripts/fetch_news.mjs https://example.com/article --search "关键词"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
91% confidence
Finding
The usage examples show very broad natural-language triggers such as '今天有什么新闻' and '看看 BBC 今天有什么新闻' with implied automatic fetching, but they do not define clear activation boundaries, user confirmation requirements, or limits on what external sources may be contacted. In an agent setting, this can cause over-triggering and unintended network access based on ambiguous user input, increasing the risk of privacy leakage, prompt abuse, or unexpected external requests.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill requires network access and can use environment-provided secrets like TAVILY_API_KEY, but it does not declare any explicit tool scope or allowed-tools policy. This weakens least-privilege controls and makes it easier for the skill to invoke broader capabilities than users or the platform may expect, especially given the browser/web fetching workflow.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest description states the skill supports 'RSS订阅', and the trigger words also include 'RSS、订阅'. However, the operational instructions in the file only describe fetching news via browser/web_fetch and using Tavily as fallback; there is no concrete RSS parsing, feed retrieval, or subscription-management behavior documented anywhere in the implementation guidance.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger phrases include extremely common terms such as '新闻', '订阅', and '阅读', which can cause the skill to activate in unrelated conversations. Because the skill can initiate network access and browsing, accidental invocation increases the chance of unintended external requests, preference reads/writes, and confusing or unsafe tool use without clear user intent.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The daily-use activation examples are vague ('today's news', 'look at the news') and allow broad interpretation, including direct access to user-specified sites. In a skill with browsing and network capabilities, this ambiguity can lead to overbroad fetching, accidental visits to unexpected domains, and execution of persistent preference logic when the user may not have intended to invoke this specific skill.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest says the skill supports RSS subscriptions, but the implementation in this file only supports direct webpage fetching and Tavily-based search. There is no RSS feed parsing, subscription management, or feed retrieval logic present here, creating a mismatch between claimed functionality and actual behavior.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's help text, usage explanations, error strings, and status messages are presented in Chinese (with only the title partially bilingual), with no option for the user to select another language. That is a natural-language policy issue because it imposes a locale/language choice without opt-in or documented justification.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
The manifest frames this skill as a news fetching and RSS subscription utility. While network access is expected, reading a credential from the environment introduces secret-handling capability that is not justified by the stated purpose alone and is not mentioned in the manifest description.

External Transmission

Medium
Category
Data Exfiltration
Content
throw new Error('需要设置 TAVILY_API_KEY 环境变量');
  }

  const response = await fetch('https://api.tavily.com/search', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
throw new Error('需要设置 TAVILY_API_KEY 环境变量');
  }

  const response = await fetch('https://api.tavily.com/search', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code posts the user's search query to the Tavily API and also fetches arbitrary user-supplied URLs over the network. While the script prints what it is doing, it does not disclose that user input and requested targets will be transmitted to external services, which is the kind of user-facing warning expected for network operations that may expose user or system data.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The natural-language interface and examples are fully Chinese, including the user prompts and feature descriptions, but there is no indication that language selection is configurable or that the skill is intentionally limited to Chinese-speaking users. This may violate language or locale policy where user opt-in or explicit justification is required.

Description-Behavior Mismatch

Low
Confidence
81% confidence
Finding
The stated purpose is news retrieval and RSS subscription/search, which is primarily read-oriented. The skill instructions add persistent configuration storage in 'CONFIG/news-preferences.md', a behavior not mentioned in the manifest description and broader than simple fetching/subscription from free sources.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"version": "1.0.0",
  "type": "module",
  "dependencies": {
    "node-fetch": "^3.3.2"
  }
}
Confidence
94% confidence
Finding
The dependency uses a caret range (^3.3.2) instead of an exact pinned version, which makes builds non-reproducible and can unexpectedly pull in newer transitive code over time. In a security-sensitive agent skill that fetches remote content, this increases supply-chain risk and makes it harder to verify whether deployed versions include security fixes or newly introduced issues.

Unverifiable Dependency: node-fetch has 3 known advisory(ies) (CVE-2022-0235 (node-fetch forwards secure headers to untrusted sites); CVE-2022-2596 (node-fetch Inefficient Regular Expression Complexity ); CVE-2020-15168 (The `size` option isn't honored after following a redirect in node-fetch)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding
The manifest references node-fetch without exact pinning, and node-fetch has known historical advisories affecting some versions. Because this skill fetches external news/RSS content, use of a dependency with unverifiable patched status creates avoidable risk around SSRF-adjacent header forwarding, ReDoS, or redirect handling issues if an affected release is installed.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script reads the TAVILY_API_KEY environment variable to authenticate outbound requests, but there is no comment or user-facing warning beyond the error when the variable is missing. For skills that access credentials or sensitive environment variables, the file should disclose that behavior so users understand the dependency and sensitivity.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/fetch_news.mjs:13