Back to skill

Security audit

zhihu-to-wechat

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly aligned with its stated WeChat draft workflow, but it asks for powerful account credentials and handles them with weak scoping and storage safeguards.

Review this skill before installing. Use it only with a dedicated WeChat service account, avoid entering secrets in chat or command-line arguments, rotate any secrets already shared, inspect generated HTML before creating drafts, and do not let untrusted image URLs flow into the publisher.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wechat_publisher.py:174
Finding

Arbitrary Image URL Fetching Enables SSRF and Local File Disclosure

Content
View full analysis
str: """从 URL 下载图片并上传到微信素材库,返回微信 CDN URL""" token = self.get_access_token() # 下载图片 print(f" ⬇️ 下载图片: {image_url[:60]}...") try: with urllib.request.urlopen(image_url, timeout=15) as resp: image_data = resp.read() content_type = resp.headers.get("Content-Type", "image/jpeg") except Exception as e: raise RuntimeError(f"图片下载失败: {e}") from e # 确定文件扩展名 ext = "jpg" if "png" in content_type: ext = "png" elif "gif" in content_type: ext = "gif" elif "webp" in content_type: ext = "webp" # 上传到微信(使用 uploadimg 接口,返回永久 URL) upload_url = f"https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token={token}" boundary = "----FormBoundaryX7MA4YWxkTrZu0gW" filename = f"image.{ext}" body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="media"; filename="{filename}"\r\n' f"Content-Type: {content_type}\r\n\r\n" ).encode("utf-8") + image_data + f"\r\n--{boundary}--\r\n".encode("utf-8") req = urllib.request.Request( upload_url, data=body, headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, method="POST", ) with urllib.request.urlopen(req, timeout=30) as resp: result = json.loads(resp.read().decode("utf-8")) ``` The same issue is present in the cover-image path: ```python def upload_cover_image(self, image_url: str) -> str: """上传封面图并返回 thumb_media_id(用于草稿接口)""" token = self.get_access_token() # 下载图片 with urllib.request.urlopen(image_url, timeout=15) as resp: image_data = resp.read() content_type ...[truncated 2540 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wechat_publisher.py:37
Finding

WeChat Access Token Stored in a Plaintext Cache Without Explicit Permission Controls

Content
View full analysis
time.time() + 300): self._access_token = cache["access_token"] self._token_expires_at = cache["expires_at"] print("📋 使用缓存 Access Token") return self._access_token except Exception: pass ``` ```python # 保存文件缓存 TOKEN_CACHE_FILE.write_text(json.dumps({ "app_id": self.app_id, "access_token": self._access_token, "expires_at": self._token_expires_at, })) ``` ### Technical Analysis The WeChat access token is a bearer credential that authorizes API operations. It is stored as plaintext in the user's home directory using `Path.write_text()`, with permissions inherited from the process umask. The code does not explicitly enforce owner-only permissions, verify the cache file's owner, reject symbolic links, or use atomic secure-file creation. In environments with a permissive umask, shared home-directory access, backups, or overprivileged monitoring agents, the token may be exposed to other local principals. The cache also includes the associated AppID and expiration time, making a stolen token immediately usable without additional discovery. ### Attack Path 1. The publisher obtains a valid WeChat access token. 2. The token is written to `~/.wechat_token_cache.json`. 3. File permissions are determined by the ambient umask rather than an explicit security policy. 4. Another local process, user, backup collector, or compromised component reads the cache. 5. The reader extracts the b ...[truncated 620 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wechat_publisher.py:10
Finding

WeChat AppSecret Can Be Exposed Through Command-Line Arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/format_article.py:216
Finding

Unsanitized Markdown and Metadata Permit HTML and Unsafe Link Injection

Content
View full analysis
' f'

▲ {desc}

' ) continue ``` ```python # 标题 if line.startswith("# "): text = line[2:].strip() html_lines.append(f'

{text}

') elif line.startswith("## "): text = line[3:].strip() html_lines.append(f'

{text}

') elif line.startswith("### "): text = line[4:].strip() html_lines.append(f'

{text}

') ``` ```python # 普通段落 else: text = apply_inline_styles(line) if text.strip(): html_lines.append(f'

{text}

') ``` ```python def apply_inline_styles(text: str) -> str: """处理行内 Markdown 格式""" # 粗体 text = re.sub(r"\*\*(.+?)\*\*", f'\\1', text) text = re.sub(r"__(.+?)__", f'\\1', text) # 行内代码 text = re.sub(r"`(.+?)`", f'\\1', text) # 斜体 text = re.sub(r"\*(.+?)\*", r'\1', text) # 链接 text = re.sub(r"\[(.+?)\]\((.+?)\)", f'\\1', text) return text ``` ```python # 标签区 tags_html = "".join(f'#{tag}' for tag in tags) # 完整 HTML html = f"""
{body_html}

{tags_html}

{build_footer(author)}
""" ``` The author is also inserted without escaping: ```python def build_footer(author: str = "IT科技号") -> str: """生成文章底部(版权+关注引导)""" return f""" ...[truncated 2400 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (45)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

声明描述的是一个多阶段自动化内容生产与发布系统,核心能力包括热点发现、文章写作、配图和微信发布。但代码片段只包含 fetch_images.py,其行为局限于图片检索与返回图片元数据,不包含任何知乎热榜访问、文本生成、微信公众号接口调用或发布逻辑。虽然“自动配图”与声明中的一个子步骤一致,但无法支撑该 skill 所宣称的整体用途,因此描述与实际行为存在明显不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个多步骤内容生产与发布系统,核心能力包括:知乎热榜选题、IT 风格文章生成、配图、以及微信服务号发布。但代码内容只包含对知乎热榜 API 的请求、结果解析、科技关键词标记和终端输出,没有任何文本生成、图片生成/获取、微信公众号接口调用、发布动作、账号凭据处理或工作流编排逻辑。因此,代码实际行为只覆盖了声明中的“获取知乎热榜并筛选科技话题”这一小部分,主功能与声明存在明显不一致。代码中的网络访问知乎 API 属于已声明工作流的支持步骤,不是额外越权能力;问题在于声明严重高估了已实现能力。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个端到端自动化内容生产与发布流程,涵盖热点采集、写作、配图和发布多个阶段。但实际代码只是一个格式化脚本:读取本地 Markdown,按预设 IT 科技风样式转成微信公众号 HTML,并替换图片占位符后输出 HTML 文件。代码没有任何网络访问、知乎抓取、内容生成、图像生成/下载、微信公众号 API 调用或发布逻辑。因此,实际行为只覆盖了“公众号文章排版/格式化”这一小部分,无法支撑所宣称的主要用途,属于明显描述与行为不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个端到端内容生产与发布流水线,覆盖热点发现、内容创作、配图和发布;但代码实际是一个较窄的微信草稿发布工具。它依赖外部提供的 HTML 文章和图片 URL,只负责与微信公众平台 API 交互完成素材上传和草稿创建。虽然“发布公众号”这一小部分有一定相关性,但核心能力大幅缺失,且最终动作也只是进入草稿箱而非直接发布,因此声明与实际行为存在明显且实质性的不匹配。

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill mandates automatic activation for broad, common phrases related to writing and publishing. Overbroad trigger rules can cause the agent to invoke networked, credential-using, or publishing workflows in contexts where the user did not intend it, increasing the chance of data exposure, unwanted external requests, or accidental content publication.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill instructs collection of sensitive credentials such as WECHAT_APP_SECRET and third-party API keys and storing them in conversation context, without explicit privacy, retention, or redaction safeguards. Conversation context is often accessible to logs, future turns, or other components, so treating secrets as ordinary chat content materially increases the risk of credential leakage and account compromise.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
78% confidence
Finding

The publication step explicitly retrieves and refreshes a WeChat access token and then uses it to upload assets and create drafts. In the context of this skill, token handling is dangerous because the same document also normalizes storing related secrets in chat context, and publication credentials provide direct control over a public-facing account.

Content

Scanner excerpt · SKILL.md (reported line 171)May include surrounding context.

md
运行脚本:`scripts/wechat_publisher.py`

流程:
1. 获取 Access Token(自动刷新)
2. 上传封面图到微信素材库 → 获取 `thumb_media_id`
3. 上传正文配图 → 替换文章中的图片 URL 为微信 CDN 地址
4. 调用草稿接口创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/wechat_api.md (reported line 5)May include surrounding context.

md
## 目录
1. [权限要求](#权限要求)
2. [Access Token](#access-token)
3. [草稿箱接口](#草稿箱接口)
4. [素材上传](#素材上传)
5. [常见错误码](#常见错误码)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/wechat_api.md (reported line 18)May include surrounding context.

md
## 目录
1. [权限要求](#权限要求)
2. [Access Token](#access-token)
3. [草稿箱接口](#草稿箱接口)
4. [素材上传](#素材上传)
5. [常见错误码](#常见错误码)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/wechat_api.md (reported line 25)May include surrounding context.

md
## 目录
1. [权限要求](#权限要求)
2. [Access Token](#access-token)
3. [草稿箱接口](#草稿箱接口)
4. [素材上传](#素材上传)
5. [常见错误码](#常见错误码)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/wechat_api.md (reported line 157)May include surrounding context.

md
## 目录
1. [权限要求](#权限要求)
2. [Access Token](#access-token)
3. [草稿箱接口](#草稿箱接口)
4. [素材上传](#素材上传)
5. [常见错误码](#常见错误码)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/wechat_api.md (reported line 161)May include surrounding context.

md
## 目录
1. [权限要求](#权限要求)
2. [Access Token](#access-token)
3. [草稿箱接口](#草稿箱接口)
4. [素材上传](#素材上传)
5. [常见错误码](#常见错误码)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 6)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 38)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 51)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 122)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 125)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 138)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 144)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/wechat_publisher.py (reported line 170)May include surrounding context.

python
wechat_publisher.py — 微信服务号草稿发布工具

功能:
1. 获取/刷新 Access Token
2. 上传图片到微信素材库
3. 替换文章中的图片链接为微信CDN地址
4. 创建草稿

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill describes operations requiring network access, local file read/write, and secret handling, but it declares no explicit tool scope or permissions boundary. That makes the operational surface ambiguous and can allow broader-than-expected execution or review bypass in hosts that rely on manifest-declared scopes for enforcement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The entire template is written as a Chinese-language article format for an IT public account and does not indicate that language selection is optional. This can violate language/locale policy when a skill should not force a specific language without user opt-in or documented regional justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring, usage instructions, and argument descriptions are presented in Chinese only, which effectively forces a single language for users interacting with the skill. The policy allows locale constraints only when justified or when users are offered a language choice, neither of which is present here.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/wechat_api.md (reported line 193)May include surrounding context.

md
"orientation": "landscape",  # 横版图,适合公众号
        "content_filter": "high",    # 过滤低质量图片
    })
    url = f"https://api.unsplash.com/search/photos?{params}"
    req = urllib.request.Request(url, headers={
        "Authorization": f"Client-ID {access_key}",
        "Accept-Version": "v1",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/fetch_images.py (reported line 61)May include surrounding context.

python
"orientation": "landscape",  # 横版图,适合公众号
        "content_filter": "high",    # 过滤低质量图片
    })
    url = f"https://api.unsplash.com/search/photos?{params}"
    req = urllib.request.Request(url, headers={
        "Authorization": f"Client-ID {access_key}",
        "Accept-Version": "v1",