Back to skill

Security audit

Rss Reader

Security checks for vulnerabilities and agentic risk

Overview

This RSS skill appears purpose-built rather than malicious, but it needs review because it can repeatedly fetch arbitrary URLs and send API keys plus RSS-derived data to configurable external services.

Install only if you are comfortable with RSS titles, links, summaries, and generated reports being sent to the AI provider and, if configured, to Feishu/Lark. Use only public feed URLs, avoid localhost/private/internal feed addresses, keep OPENAI_BASE_URL fixed to a trusted provider, protect the API key and webhook values, and review the cron command before enabling automatic refreshes.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
rss_reader.py:33
Finding
API Credential Disclosure Through an Unrestricted AI Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `rss_reader.py:33-35, 349-354, 596-604` **Vulnerability Type**: User-configurable credential transmission destination **Risk Level**: High ### Vulnerable Code ```python OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") OPENAI_BASE_URL = os.getenv( "OPENAI_BASE_URL", "https://open.bigmodel.cn/api/paas/v4" ) FEISHU_WEBHOOK_URL = os.getenv("FEISHU_WEBHOOK_URL", "") ``` ```python response = requests.post( f"{OPENAI_BASE_URL}/chat/completions", headers={ "Authorization": f"Bearer {OPENAI_API_KEY}", "Content-Type": "application/json" }, ``` The daily-report implementation has the same issue: ```python base_url = os.getenv( "OPENAI_BASE_URL", "https://open.bigmodel.cn/api/paas/v4" ) api_url = ( base_url if base_url.endswith("/chat/completions") else f"{base_url}/chat/completions" ) response = requests.post( api_url, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, ``` ### Technical Analysis The Skill obtains an AI API credential from `OPENAI_API_KEY` and transmits it as a bearer token to a destination derived directly from `OPENAI_BASE_URL`. The code does not validate the URL scheme, hostname, port, resolved address, redirect target, or trusted provider identity. Sending a credential to the configured AI provider is necessary for the declared summarization feature. However, allowing the credential destination to be any arbitrary URL exceeds the minimum privilege required. Anyone able to modify the Gateway environment or process environment can redirect the request to a server they control. The same unrestricted destination is used by single-article summaries, batch summaries, and daily reports. Requests may also contain article titles, descriptions, and source metadata. ### Attack Path 1. The attacker gains an ability to influence the Skill's environment or Gateway configuration. 2. The ...[truncated 977 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require an HTTPS endpoint and reject plaintext HTTP. 2. Maintain an explicit allowlist of supported provider origins, including the expected scheme, hostname, and port. 3. Parse URLs with `urllib.parse.urlparse` rather than validating them with string prefixes. 4. Reject embedded credentials, fragments, unexpected ports, IP literals, localhost, and private, loopback, link-local, multicast, or reserved addresses. 5. Disable redirects or validate every redirect destination before forwarding a request containing credentials. 6. Resolve the hostname and validate all returned addresses immediately before connecting to mitigate DNS rebinding. 7. Use provider-specific credential variables so a credential intended for one provider cannot be sent to another provider. 8. Avoid placing bearer tokens into a request until the final destination has passed validation. 9. Document that changing the endpoint changes the party receiving both the API key and submitted content. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
rss_reader.py:190
Finding
Server-Side Request Forgery Through User-Supplied RSS URLs<![CDATA[ ## Vulnerability Details **File Location**: `rss_reader.py:190-195, 272-279, 750-753` **Vulnerability Type**: Unrestricted server-side URL retrieval **Risk Level**: High ### Vulnerable Code The subscription URL is accepted directly from a command argument: ```python if command == "订阅": if len(sys.argv) < 3: print("❌ 请提供 RSS 地址") print("用法:python rss_reader.py 订阅 <url> [名称]") return url = sys.argv[2] name = sys.argv[3] if len(sys.argv) >= 4 else None print(add_subscription(url, name)) ``` The URL is then fetched without destination validation: ```python def add_subscription(url: str, name: str = None): """添加订阅""" subscriptions = load_json(SUBSCRIPTIONS_FILE, {}) if url in subscriptions: return f"⚠️ 已存在订阅:{subscriptions[url]['name']}" # 解析 RSS 获取名称 try: feed = feedparser.parse(url) if feed.bozo and not feed.entries: return f"❌ 无效的 RSS 地址:{url}" feed_name = name or feed.feed.get("title", url) ``` Stored subscription URLs are fetched again during refresh: ```python def fetch_new_articles(url: str): """获取新文章""" subscriptions = load_json(SUBSCRIPTIONS_FILE, {}) articles = load_json(ARTICLES_FILE, {}) try: feed = feedparser.parse(url) if feed.bozo and not feed.entries: print(f" ⚠️ 解析失败:{url}") return [] ``` ### Technical Analysis The Skill legitimately needs network access to retrieve RSS feeds. However, it passes an unrestricted command-supplied URL to `feedparser.parse()` without enforcing an HTTP(S) scheme or validating the destination hostname and resolved IP address. Consequently, the Skill may be induced to connect to localhost, private network services, link-local services, or cloud metadata endpoints. Redirects and DNS rebinding may also allow an initially acceptable hostname to resolve or redirect to a restricted destination. A successfully added URL is persisted in `subscripti ...[truncated 1721 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse all feed URLs with a standards-compliant URL parser. 2. Allow only `https` and, if operationally necessary, explicitly approved `http` URLs. 3. Reject missing hosts, embedded credentials, unexpected ports, IP literals, and malformed URLs. 4. Resolve the destination hostname and reject every loopback, private, link-local, multicast, unspecified, and reserved address. 5. Repeat address validation immediately before connection and after every DNS resolution. 6. Disable automatic redirects or validate each redirect target using the same policy. 7. Consider an explicit feed-domain allowlist for automated or privileged deployments. 8. Enforce connection and read timeouts, response-size limits, and a maximum redirect count. 9. Store a URL only after it passes validation, and revalidate stored URLs before every refresh. 10. Prevent fetched feed fields from being treated as trusted Markdown or instructions when forwarded to AI or messaging systems. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unpinned Third-Party Dependencies Permit Unreviewed Future Releases<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-2` **Vulnerability Type**: Non-reproducible dependency resolution **Risk Level**: Low ### Vulnerable Code ```text feedparser>=6.0.0 requests>=2.28.0 ``` ### Technical Analysis The dependency names correspond to established packages, and the audit found no evidence of typosquatting, dependency confusion, or an intentionally malicious package. Nevertheless, minimum-only version constraints permit package installers to select any future release satisfying the range. As a result, the installed code may differ from the versions reviewed with this Skill. A compromised, malicious, or incompatible future release could be downloaded and imported without a source-code change in the project. The absence of package hashes also prevents integrity verification against a reviewed artifact. ### Attack Path 1. A user installs the Skill's dependencies with `pip install -r requirements.txt`. 2. The package index resolves each `>=` constraint to a newer release available at installation time. 3. A compromised or otherwise unsafe release satisfies the declared constraint. 4. The package is downloaded and installed without hash verification. 5. Package installation or later import executes code from the unreviewed release in the user's Python environment. This path requires compromise or malicious publication involving a dependency or its distribution channel; no such compromise was identified during this audit. ### Impact Assessment A malicious dependency release could execute code with the privileges of the user installing or running the Skill. That could expose environment variables, including `OPENAI_API_KEY` and `FEISHU_WEBHOOK_URL`, modify local files accessible to the process, or make arbitrary network requests. The current evidence establishes a supply-chain hardening weakness rather than a presently malicious dependency. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin each dependency to a reviewed exact version. 2. Generate and verify cryptographic hashes for all packages and transitive dependencies. 3. Use a lock file or hash-locked requirements file created by a tool such as `pip-tools`. 4. Install with hash enforcement, for example `pip install --require-hashes`. 5. Review dependency updates through a controlled process with vulnerability scanning and tests. 6. Use an approved package index and prevent fallback to untrusted indexes. 7. Periodically refresh pins so security fixes are adopted without permitting uncontrolled upgrades. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tainted flow: 'OPENAI_BASE_URL' from os.getenv (line 34, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
content_clean = re.sub(r'<[^>]+>', '', content)[:1500]
    
    try:
        response = requests.post(
            f"{OPENAI_BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {OPENAI_API_KEY}",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'OPENAI_BASE_URL' from os.getenv (line 34, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
content_clean = re.sub(r'<[^>]+>', '', content)[:1500]
    
    try:
        response = requests.post(
            f"{OPENAI_BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {OPENAI_API_KEY}",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'FEISHU_WEBHOOK_URL' from os.getenv (line 35, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
    
    try:
        response = requests.post(
            FEISHU_WEBHOOK_URL, 
            json=message, 
            timeout=10
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'api_url' from os.getenv (line 597, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
base_url = os.getenv("OPENAI_BASE_URL", "https://open.bigmodel.cn/api/paas/v4")
        api_url = base_url if base_url.endswith("/chat/completions") else f"{base_url}/chat/completions"
        
        response = requests.post(
            api_url,
            headers={
                "Authorization": f"Bearer {api_key}",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'webhook_url' from os.getenv (line 666, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
    
    try:
        response = requests.post(webhook_url, json=payload, timeout=10)
        if response.status_code == 200:
            result = response.json()
            if result.get("StatusCode") == 0:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
This markdown file instructs users to initialize a Git repository and push the entire skill directory to a public hosting service, but it does not warn users to review files for secrets, local data, or generated artifacts before publishing. Because this action can affect privacy and data integrity, a user-facing warning is expected in the documentation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README explicitly documents configuring a Feishu webhook for pushing RSS-derived summaries, but it does not clearly warn users that article content, extracted summaries, or metadata may be transmitted to an external third-party service. In a skill that also performs AI summarization and automated scheduled refreshes, this can lead to unintentional disclosure of internal or sensitive feed content, especially if users subscribe to private or semi-private RSS sources.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documentation describes capabilities that require environment-variable access, filesystem reads/writes, and network access, but it does not declare any explicit tool scope or permissions boundary. This creates an authorization mismatch: users and platforms cannot easily understand or constrain what the skill may access, increasing the risk of unintended data exposure or over-privileged execution.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill description emphasizes AI summarization and Feishu push, but it does not clearly warn users that article metadata and generated summaries will be transmitted to third-party AI and messaging services. This is dangerous because users may invoke the skill assuming local processing, while the skill can disclose reading interests, selected sources, titles, and summary content to external providers.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger phrases are broad enough to match ordinary conversation about subscriptions, summaries, or RSS-like topics, which can cause the skill to activate unexpectedly. In this skill's context, unintended activation is more dangerous because activation can lead to external network requests, automatic subscription creation, article processing, and possible Feishu/AI transmission without sufficiently explicit user intent.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
export OPENAI_API_KEY="sk-xxx"
export OPENAI_BASE_URL="https://api.openai.com/v1"
```

**💡 智谱 AI 优势:**
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The manifest text, including the description and all declared commands, is exclusively in Chinese. This can amount to a language/locale policy issue because the skill appears to require a specific language without documenting opt-in, alternatives, or a justified region-specific scope.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill transmits article content and metadata to an external AI provider when generating summaries, but there is no explicit runtime warning or consent gate at the point of execution. In a content-aggregation skill, feeds may include proprietary, internal, or sensitive subscription sources, so silent exfiltration to a third party creates a real privacy and compliance risk.

External Transmission

Medium
Category
Data Exfiltration
Content
content_clean = re.sub(r'<[^>]+>', '', content)[:1500]
    
    try:
        response = requests.post(
            f"{OPENAI_BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {OPENAI_API_KEY}",
Confidence
86% confidence
Finding
This call sends article content to an external AI service. In this skill's context, outbound transmission is core functionality, but it is still a genuine data-exposure surface because RSS items may contain sensitive text and the transfer occurs without minimizing or classifying content sensitivity.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The summarization prompt explicitly instructs the model to respond in concise Chinese, and the rest of the CLI/help text is also fixed in Chinese. This imposes a language choice without user opt-in, which matches the policy-violation category for locale or language constraints.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The batch summarization path instructs the model in Chinese and requests concise Chinese summaries for all articles. Because no language preference mechanism is provided, the skill enforces a specific language by default rather than offering user choice.

External Transmission

Medium
Category
Data Exfiltration
Content
prompt += "\n\n请按以下格式输出:\n1. [摘要内容]\n2. [摘要内容]\n..."
    
    try:
        response = requests.post(
            f"{OPENAI_BASE_URL}/chat/completions",
            headers={
                "Authorization": f"Bearer {OPENAI_API_KEY}",
Confidence
86% confidence
Finding
The batch-summary path transmits multiple article titles and summaries to an external AI service, increasing the volume of exposed data in a single request. Although intended behavior, it represents a real privacy/compliance risk when feeds include non-public or sensitive material.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(
            FEISHU_WEBHOOK_URL, 
            json=message, 
            timeout=10
Confidence
83% confidence
Finding
This webhook send transmits article title, summary, source, and link to an external collaboration platform. Because the skill automatically distributes content outside the local environment, it creates a real information-sharing risk if feeds or generated summaries contain sensitive or internal material.

External Transmission

Medium
Category
Data Exfiltration
Content
base_url = os.getenv("OPENAI_BASE_URL", "https://open.bigmodel.cn/api/paas/v4")
        api_url = base_url if base_url.endswith("/chat/completions") else f"{base_url}/chat/completions"
        
        response = requests.post(
            api_url,
            headers={
                "Authorization": f"Bearer {api_key}",
Confidence
87% confidence
Finding
The daily-report generation sends a consolidated set of article titles and sources to an external AI provider. Aggregated metadata can itself be sensitive because it reveals monitoring interests, internal topics, or research focus, making this more than a harmless network call.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code automatically pushes the generated report, including article titles and links, to Feishu without an explicit execution-time confirmation. This can leak aggregated reading activity or sensitive feed contents to a broader audience than intended, especially because report push happens automatically after refresh/report flows.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(webhook_url, json=payload, timeout=10)
        if response.status_code == 200:
            result = response.json()
            if result.get("StatusCode") == 0:
Confidence
84% confidence
Finding
This sends the full generated report to Feishu, potentially exposing aggregated insights, titles, and links to a configured external destination. In this skill, automatic post-refresh/report pushing makes the risk more concrete because distribution can happen without an additional human review step.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
A natural-language policy issue exists because the skill documentation forces a specific language presentation for all users without any opt-in or alternative language path. The file does not indicate that the skill is region-specific or that Chinese is required for compliance or audience constraints.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
Natural-language instructions, commands, and operational guidance are written entirely in Chinese, including the invocation examples and cron prompt text. There is no indication that the skill is region-specific or that alternative language support is available, which can violate a language-choice policy.

Missing User Warnings

Low
Confidence
84% confidence
Finding
Although the file states that 18 feeds are auto-added on first use, it is presented as a feature rather than a caution about modifying the user's subscription state. Since this changes stored user data/configuration automatically, a clearer warning would better disclose the behavior.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The design section explicitly states '单篇摘要 ❌(已移除)' and '不做单篇摘要', indicating the skill no longer performs per-article summaries. However, later documentation reintroduces 'AI 摘要' as an active feature in the feature table, FAQ, changelog, and examples, which contradicts the stated intent and can mislead users about what the skill actually does.

Static analysis

No suspicious patterns detected.