Back to skill

Security audit

信息源监控助手

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent with its monitoring purpose, but it needs review because it can fetch arbitrary URLs, send collected content to external services, and store an LLM API key in plain text.

Install only if you are comfortable with a Chinese-focused monitoring tool that fetches configured sources and may send article content or summaries to an LLM provider and a Feishu webhook. Avoid entering an LLM API key into the setup wizard unless the plaintext config storage is fixed; prefer environment variables or a secret store. Do not add internal, localhost, cloud metadata, or untrusted URLs as sources, and review the webhook and LLM endpoints before enabling them.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup.py:136
Finding
LLM API key is persisted in plaintext configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.py:136-146` and `scripts/setup.py:196-211` **Vulnerability Type**: Plaintext credential storage **Risk Level**: High ### Vulnerable Code ```python api_key = input(" API Key (留空使用环境变量 LLM_API_KEY): ").strip() base_url = input(" API Base URL [https://dashscope.aliyuncs.com/compatible-mode/v1]: ").strip() model = input(" 模型名称 [qwen-plus]: ").strip() llm_config = {} if api_key: llm_config["api_key"] = api_key os.environ["LLM_API_KEY"] = api_key if base_url: llm_config["base_url"] = base_url or "https://dashscope.aliyuncs.com/compatible-mode/v1" os.environ["LLM_API_BASE_URL"] = llm_config["base_url"] if model: llm_config["model"] = model or "qwen-plus" os.environ["LLM_MODEL"] = llm_config["model"] ``` ```python def save_config(sources, keywords, channels, llm_config): """保存所有配置""" CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(SOURCES_FILE, "w", encoding="utf-8") as f: json.dump(sources, f, ensure_ascii=False, indent=2) settings = { "keywords": keywords, "channels": channels, "llm": llm_config, "configured_at": __import__("datetime").datetime.now().isoformat(), "version": "1.0.0" } with open(SETTINGS_FILE, "w", encoding="utf-8") as f: json.dump(settings, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis When a user enters an LLM API key, the setup routine places it in `llm_config`. The entire object is subsequently serialized into `config/settings.json`, resulting in unencrypted credential persistence inside the project directory. This persistence is unnecessary for the current implementation because `scripts/summarizer.py` reads the API key from `LLM_API_KEY` rather than loading it from `settings.json`. Consequently, the code creates credential exposure without providing functional persistence after the setup process exits. No restrictive file permis ...[truncated 1185 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not add `api_key` to `llm_config` or serialize it into `settings.json`. 2. Store only non-sensitive settings such as the model name and API base URL. 3. Continue reading the credential from `LLM_API_KEY`, or integrate an operating-system credential store. 4. If file-based secret storage is unavoidable, place it outside the project, restrict permissions to the owner, and clearly identify it as sensitive. 5. Add generated configuration and secret files to `.gitignore`. 6. On startup, detect legacy plaintext keys, migrate them to an approved secret store, and remove them from the JSON file. 7. Document the credential-handling and rotation requirements. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fetcher.py:91
Finding
Unrestricted source URLs permit server-side request forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.py:76-88` and `scripts/fetcher.py:91-108, 128-145` **Vulnerability Type**: Server-side request forgery through user-controlled fetch targets **Risk Level**: High ### Vulnerable Code ```python name = input(" 名称: ").strip() or "自定义源" url = input(" URL/RSS地址: ").strip() stype = input(" 类型(rss/webpage/wechat) [rss]: ").strip().lower() or "rss" keywords_str = input(" 关键词(逗号分隔,留空跳过): ").strip() keywords = [k.strip() for k in keywords_str.split(",") if k.strip()] if keywords_str else [] sources.append({ "name": name, "type": stype, "url": url, "keywords": keywords, "enabled": True }) ``` ```python def fetch_webpage(source_config, proxy=None): """采集普通网页(提取正文) 使用简单的正则提取正文内容,不需要额外依赖。 如果有 readability 或 newspaper3k 会更好,但不强制要求。 """ if not HAS_REQUESTS: print("[WARN] requests未安装,跳过网页源: {}".format(source_config.get("name"))) return [] try: proxies = {"http": proxy, "https": proxy} if proxy else None resp = requests.get( source_config["url"], timeout=15, headers={"User-Agent": "Mozilla/5.0 (compatible; InfoMonitor/1.0)"}, proxies=proxies ) resp.raise_for_status() html = resp.text ``` ```python def fetch_wechat(source_config, proxy=None): """采集微信公众号文章 微信公众号文章URL格式: https://mp.weixin.qq.com/s/xxxxx 需要处理反爬:建议用户使用代理或官方API模式 """ if not HAS_REQUESTS: print("[WARN] requests未安装,跳过微信源: {}".format(source_config.get("name"))) return [] try: proxies = {"http": proxy, "https": proxy} if proxy else None resp = requests.get( source_config["url"], timeout=15, headers={ "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)", "Referer": "https://mp.weixin.qq.com/" }, proxies=proxies ...[truncated 2103 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only explicitly required URL schemes, preferably HTTPS. 2. Resolve each hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 addresses. 3. Explicitly block cloud metadata destinations and hostnames. 4. Disable automatic redirects or validate every redirect target with the same policy. 5. Consider an allowlist of approved public domains for predefined and custom sources. 6. Validate that WeChat sources use the expected `mp.weixin.qq.com` host. 7. Restrict destination ports to standard web ports unless a user explicitly approves an exception. 8. Apply response-size and content-type limits to reduce resource exhaustion. 9. If proxy support is enabled, document that a proxy can observe fetched URLs and content, and ensure destination policy is enforced independently of the proxy. 10. Warn users before sending fetched content to an LLM or webhook, particularly when custom sources are configured. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/license.py:97
Finding
Predictable prefixes and universal keys bypass paid-license authorization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dedup.py:115-124` and `scripts/license.py:97-119` **Vulnerability Type**: Improper authorization and insecure offline license validation **Risk Level**: Medium ### Vulnerable Code ```python def activate_license(key): """激活授权码(离线验证)""" state = load_state() simple_keys = ["CNIM-2026-PRO", "CNIM-TEST-KEY"] if key in simple_keys or key.startswith("CNIM-"): state["plan"] = "pro" state["license_key"] = key save_state(state) return True return False ``` ```python def activate(key): """激活授权码 Args: key: str, 授权码格式 CNIM-XXXX-XXXX Returns: bool: 是否激活成功 """ state = load_state() valid_prefixes = ["CNIM-2026-", "CNIM-PRO-", "CNIM-TEST"] if any(key.startswith(p) for p in valid_prefixes) or key == "CNIM-FREE-UNLOCK": state["plan"] = "pro" state["license_key"] = key state["activated_at"] = datetime.now().isoformat() save_state(state) print("✅ 激活成功!欢迎成为专业版用户。") print(" 授权码: {}".format(key)) print(" 套餐: 专业版") return True ``` ### Technical Analysis The Skill treats easily guessed string prefixes as proof of entitlement. `dedup.activate_license()` is especially broad and accepts every value beginning with `CNIM-`. The primary license module also accepts arbitrary values beginning with known prefixes and includes the hard-coded universal value `CNIM-FREE-UNLOCK`. There is no cryptographic signature, message authentication code, checksum tied to an issued entitlement, or online verification. The state file is also locally writable, so a user can directly change `"plan": "free"` to `"plan": "pro"` even without invoking the activation functions. The presence of two inconsistent validation implementations increases the likelihood that a weaker routine will be used unintentionally. ### Attack Path 1. A user invokes the activation command with a guess ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace prefix matching with cryptographically signed offline license documents. 2. Embed only a public verification key in the Skill and sign issued entitlements with a separately protected private key. 3. Include the plan, expiry, customer or installation identifier, and license version in the signed payload. 4. Remove `CNIM-FREE-UNLOCK`, test keys, and wildcard prefix acceptance from production code. 5. Consolidate license validation into one authoritative implementation. 6. If stronger revocation and concurrency controls are required, verify licenses through an authenticated entitlement service over TLS. 7. Treat local quota controls as tamper-evident rather than tamper-proof; signed state or server-side usage accounting is required where enforcement has financial significance. 8. Add tests confirming that malformed, truncated, guessed, and modified licenses are rejected. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (25)

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

Critical
Category
Data Flow
Content
}
        }
        
        resp = requests.post(webhook_url, json=payload, timeout=10)
        resp.raise_for_status()
        print("[OK] 已推送到飞书")
        return True
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding indicates outbound LLM API calls via environment-configured endpoints that are not declared in permissions, while the claimed monitoring and push pipeline is not actually implemented. Hidden external transmission of collected content is a meaningful privacy and security risk, especially for industry monitoring workflows that may involve sensitive or proprietary material.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
This finding indicates outbound LLM API calls via environment-configured endpoints that are not declared in permissions, while the claimed monitoring and push pipeline is not actually implemented. Hidden external transmission of collected content is a meaningful privacy and security risk, especially for industry monitoring workflows that may involve sensitive or proprietary material.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
This finding indicates outbound LLM API calls via environment-configured endpoints that are not declared in permissions, while the claimed monitoring and push pipeline is not actually implemented. Hidden external transmission of collected content is a meaningful privacy and security risk, especially for industry monitoring workflows that may involve sensitive or proprietary material.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding indicates outbound LLM API calls via environment-configured endpoints that are not declared in permissions, while the claimed monitoring and push pipeline is not actually implemented. Hidden external transmission of collected content is a meaningful privacy and security risk, especially for industry monitoring workflows that may involve sensitive or proprietary material.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill advertises capabilities that imply network access, environment variable use, and local file I/O, but it does not declare any tool scope or permissions boundary. In agent ecosystems, missing explicit scope increases the chance of overbroad runtime access and makes it harder for users or reviewers to understand what the skill can actually do.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The example invocation “帮我监控这几个公众号” is a natural-language trigger that is broad and lacks constraints about scope, source types, or required context. The document does not provide negative examples or clearer activation boundaries, so this phrasing could collide with ordinary conversational requests.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation mentions AI summarization and webhook integrations but does not clearly warn that monitored content may be transmitted to external LLM or messaging services. For a monitoring skill handling potentially sensitive business intelligence, undisclosed third-party transmission materially increases privacy and data leakage risk.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This JSON manifest is dominated by explicitly Chinese-language sources such as "36氪", "虎嗅网", and "InfoQ中文", which indicates a locale/language preference baked into the skill configuration. Because the file provides no natural-language notice, opt-in, or alternative language selection, it can violate the policy against forcing a specific language or locale without user choice.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The module docstring describes the skill entirely in Chinese, and the command-line help text later in the file is also Chinese-only. This imposes a specific language/locale on users without offering a language choice or documenting a justified regional constraint.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest describes a skill that monitors information sources, summarizes content, and pushes results to messaging channels. In addition to URL deduplication, this file persists usage counters, enforces free-tier quotas, and activates a local license key, which are product licensing behaviors not implied by the stated monitoring workflow.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The offline license activation accepts any key beginning with "CNIM-", which makes privilege escalation to the pro plan trivial and defeats any meaningful authorization check. While this is primarily a business-logic flaw rather than a host-compromise issue, it enables unauthorized feature access and shows intentionally weak access control around paid functionality.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring and all operational messages are written only in Chinese, which imposes a specific language/locale on users. The file does not offer an opt-in language choice or explain that the skill is intentionally limited to a Chinese-speaking or region-specific context.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The file implements a local freemium licensing and activation system that is outside the declared monitoring/summarization/push functionality of the skill. This hidden monetization and feature-gating behavior changes the trust and operating model of the skill, and because activation is enforced purely through local state and hardcoded prefixes, it can mislead users about capability, provenance, and support expectations.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring and all user-facing instructions are written only in Chinese, and the command/help output likewise assumes Chinese-language users. The file does not offer an opt-in language choice or explain that the skill is intentionally limited to a Chinese-speaking or region-specific audience.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
This code file contains user-facing natural-language strings in Chinese, beginning with the module description and continuing throughout console output. Under the policy rule, forcing a specific language without user opt-in can be a locale-policy violation when no alternative or justification is provided.

External Transmission

Medium
Category
Data Exfiltration
Content
}
        }
        
        resp = requests.post(webhook_url, json=payload, timeout=10)
        resp.raise_for_status()
        print("[OK] 已推送到飞书")
        return True
Confidence
80% 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
96% confidence
Finding
The module docstring and all user-facing prompts are written exclusively in Chinese, presenting the setup flow as a fixed Chinese-language experience. The policy only permits locale constraints when the skill offers a language choice or clearly documents and justifies the restriction, which is not present here.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
"keywords": keywords,
        "channels": channels,
        "llm": llm_config,
        "configured_at": __import__("datetime").datetime.now().isoformat(),
        "version": "1.0.0"
    }
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The function sends article content and prompts to a third-party LLM endpoint configured by environment variable, but this file contains no consent gate, redaction step, or warning about external transmission. In a monitoring/summarization skill, sources may include proprietary, personal, or otherwise sensitive text, so silent export to external services can create confidentiality and compliance risk.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The prompt template says the model will summarize the article content shown under '内容(前2000字)', but the format call provides `content_trunc` while the template expects `content_truncated`. As written, the documented behavior of summarizing article content is contradicted by the implementation, which will fail before inserting the content.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The prompt requires a Chinese summary and the system prompt enforces JSON-only output tailored to that instruction, while the module description also emphasizes Chinese summaries. This imposes a specific language behavior without user opt-in or a documented region-specific justification.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The skill states that output may be written to ~/info-digest/ but does not clearly warn users that monitored content will be persisted locally. This can expose sensitive summaries or source material to other local users, backups, or later unintended access.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The top-level docstring is written only in Chinese and presents the skill as Chinese-specific, with no indication that language choice is optional or that the locale restriction is required for a region-specific purpose. This is a natural-language policy concern because it implicitly fixes the skill's language/locale without user opt-in.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The file’s primary natural-language description is entirely in Chinese and does not indicate that language is configurable or limited to a justified region-specific use case. Per the policy, forcing a specific language without user opt-in can be a locale-policy violation.

Static analysis

No suspicious patterns detected.