T09 · Insecure Skill Coding Practices
- Location
fetcher.py:5- Finding
Server-Side Request Forgery Through Insufficient URL Validation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill largely matches its social-media review purpose, but its URL fetching and local review server are under-protected enough that crafted input or local web content could read or change review data or trigger LLM calls.
Review before installing. Use only trusted, exact X/Twitter status URLs, avoid running the review server while browsing untrusted sites, and do not process private or regulated content unless you are comfortable sending it to the configured fetch and LLM providers. Prefer a fixed trusted LLM endpoint, a temporary API key, and local firewall or sandbox controls until the URL validation and review API authentication are fixed.
fetcher.py:5Server-Side Request Forgery Through Insufficient URL Validation
notifier.py:71Unauthenticated Local Review API Exposed to Arbitrary Web Origins
notifier.py:332Stored HTML and Script Injection Through Unescaped Original URL
${escapeHtml(d.commentary || '')}
The code chunk implements a narrowly scoped fetcher for a single X/Twitter post or article. It parses X/Twitter URLs, calls api.fxtwitter.com and cdn.syndication.twimg.com, and returns structured content such as text, author, media, and engagement counts. It does not contain any scheduling, batch processing, patrol/monitoring, notification delivery, or LLM-based distillation behavior described in the declaration. While the declared description includes single post reading, the broader claimed capabilities are not represented in this code, making the description materially broader than the actual behavior shown.
The declared description emphasizes social media scraping/automation, real-time post reading, scheduled batch patrol, LLM distillation, and review notifications. The supplied code does not perform scraping, post reading, or scheduling. Instead, its primary purpose is a local human-in-the-loop review interface for already-generated tweet commentary drafts, with persistence to local JSON files and optional regeneration via an LLM. While 'review notifications' is partially represented by the desktop popup/open-browser behavior, that is only a small subset of the actual functionality. The code therefore has a materially different primary purpose and includes undeclared capabilities such as hosting an HTTP server and mutating local draft/archive state.
The declared description presents a broader social-media scraping and automation skill with multiple capabilities: reading posts in real time, scheduled patrol, LLM distillation, and review notifications. The supplied code chunk implements only one subset of that description: LLM distillation of already-collected tweet/article data from a local JSON file. It does not scrape content, connect to social platforms, schedule patrols, process triggers, or send notifications. Because the actual code’s primary purpose is narrower and materially different from the declared end-to-end automation description, this is a description-behavior mismatch.
The skill documents capabilities that involve environment variables, local file reads/writes, network access, and shell execution, but it does not declare any explicit tool scope or permissions boundary. This is dangerous because an agent or runtime may grant broader access than users expect, increasing the risk of unintended file modification, external requests, or command execution during normal use.
Pipeline mode sends scraped post content to an external LLM API, but the documentation omits a privacy warning and data-handling notice. This is dangerous because users may unknowingly transmit monitored content, potentially including sensitive or regulated data, to a third-party service.
The code sends user-supplied X/Twitter URLs directly to api.fxtwitter.com, a third-party service, and also sends tweet identifiers to X syndication endpoints without any visible consent, disclosure, or minimization. In a social-media scraping skill, this matters because queried URLs can reveal user interests, monitoring targets, or investigation subjects to external services, creating a privacy and data-handling risk even if no code execution occurs.
The module docstring and embedded UI text are entirely in Chinese and the HTML explicitly sets lang="zh-CN", which indicates the skill is designed to operate in a fixed locale. There is no visible mechanism offering users a language choice or documenting that the locale restriction is intentional and limited to a region-specific use case.
The manifest describes scraping, scheduled patrol, LLM distillation, and review notifications. While notifications are in scope, this implementation uses OS-level PowerShell subprocess execution, which is a broader host-execution capability than is obviously required for that purpose. A local review UI could notify without invoking shell-capable system tooling.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
try:
import subprocess
msg = f"共 {len(pending)} 条推文锐评待审阅"
subprocess.Popen(
["powershell", "-Command",
f"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null; "
f"[System.Windows.Forms.MessageBox]::Show('{msg}', '推文审阅提醒')"],
The top-level documentation describes reading pending_tweets.json, calling the LLM, and outputting to drafts.json, which implies a transformation/output step only. In addition, the implementation clears pending_tweets.json after processing, which is a material side effect not disclosed by the docstring.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
配置方式(环境变量):
LLM_API_KEY - API 密钥(必须)
LLM_BASE_URL - API 端点(默认 https://api.openai.com/v1)
LLM_MODEL - 模型名称(默认 gpt-4o-mini)
"""
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
配置方式(环境变量):
LLM_API_KEY - API 密钥(必须)
LLM_BASE_URL - API 端点(默认 https://api.openai.com/v1)
LLM_MODEL - 模型名称(默认 gpt-4o-mini)
"""
The built-in system prompt explicitly instructs the model to '使用中文输出', which imposes a language choice. The file does not offer a user-selectable language option or indicate that this locale restriction is optional or justified as region-specific.
The base URL for the LLM API is fully controlled by an environment variable with no validation or allowlisting. This allows redirection of all processed content and the Authorization bearer token to an arbitrary endpoint, creating a strong exfiltration and credential-leak risk if the environment is misconfigured or attacker-influenced.
return {
"api_key": api_key,
"base_url": os.environ.get("LLM_BASE_URL", "https://api.openai.com/v1"),
"model": os.environ.get("LLM_MODEL", "gpt-4o-mini"),
}
The code performs outbound HTTP transmission of model prompts containing tweet/article content to an external API. In the context of a social-content processing skill, this materially increases privacy and confidentiality risk because untrusted or sensitive collected content can be exfiltrated outside the local environment by default.
}
try:
resp = requests.post(
f"{config['base_url']}/chat/completions",
headers=headers,
json=payload,
Tweet and article content is sent to a third-party LLM endpoint without any explicit user-facing notice, consent, or data-classification guardrails. If the queued content includes private, sensitive, embargoed, or regulated material, this causes unreviewed external data disclosure to a provider selected by configuration.
The manifest describes a broader social media scraping and automation skill with real-time reading, scheduled batch patrol, distillation, and review notifications. This file's actual behavior is narrower and different in emphasis: it reads a local pending_tweets.json queue, calls an LLM, writes drafts.json, and clears the pending queue, with no scraping, patrol scheduling, or notification behavior present here.
The prompt implements social-media post generation with a strong persona and opinionated output, which materially differs from the declared skill purpose of reading, scraping, distillation, and review notification. This kind of capability mismatch is dangerous because it can cause the agent to generate unsolicited public-facing content or persuasive messaging in workflows that users expect to be passive analysis, increasing the risk of misuse, brand harm, or unintended automation.
This file's natural-language docstrings and console messages are presented exclusively in Chinese, with no indication that users may select another language or locale. The policy for this category flags language-forcing behavior when a specific language is imposed without user opt-in or documented justification.
This JSON file contains multiple human-facing labels written entirely in Chinese, with no indication that the skill supports user language selection or that it is intentionally limited to a Chinese-speaking locale. Under the policy for natural-language violations, forcing a specific language without user opt-in can be a locale policy issue.
The module docstring presents the skill name, usage instructions, and output description entirely in Chinese, with no indication that other languages are supported or that the user can choose a locale. This is a natural-language locale policy concern because the skill appears to impose a specific language by default rather than offering an opt-in or documented justification.
The skill states that pipeline mode starts a local HTTP review server and opens a browser page, but it does not present this as a clear user warning. This is dangerous because local services and automatic browser launches expand the attack surface and may surprise users in restricted or multi-user environments.
The file's natural-language docstrings, comments, and user-visible error messages are written exclusively in Chinese, which imposes a specific language choice without any indication of user opt-in or locale justification. Under the language/locale policy, a skill should not force a language unless it is documented as intentional and context-specific.
This plain text file says it contains 'Twitter URL input list' and shows a single tweet URL example, but it does not clearly constrain what kinds of URLs are allowed beyond that example. Without an explicit statement such as 'tweet/status URLs only' or disallowed examples, the trigger/input scope is underspecified and could lead to unintended inputs being treated as valid.
The inline documentation presents notify() as a simple startup helper with three actions, but the actual function additionally enters a long-running HTTP server loop and exposes mutable API endpoints until interrupted. This is more than missing detail because the documented intent understates the persistent server behavior and side effects.
No suspicious patterns detected.