Back to skill

Security audit

Wechat Native Publisher

Security checks for vulnerabilities and agentic risk

Overview

This WeChat drafting skill mostly matches its stated purpose, but it needs review because it handles account credentials and draft creation while exposing broad override and plugin-style execution paths.

Install only if you are comfortable giving it WeChat official-account credentials and letting it create drafts and upload media. Avoid --force and --no-validate for normal use, do not use --build with untrusted module names, and treat generated previews containing remote images as capable of making browser network requests.

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

Warning
Location
scripts/push_draft.py:564
Finding

Failed HTML image processing silently preserves third-party tracking URLs

Content
View full analysis
,上传并替换为微信 URL print("[2/5] 正文插图 -> 素材库") html = body_html img_count = 0 for img_m in re.finditer(r']*\bsrc="([^"]+)"[^>]*>', html, flags=re.I): src_raw = img_m.group(1).strip() # 转义形态(匹配 html 原文,用于替换) src = re.sub(r"[\x00-\x1f\x7f]+", "", html.unescape(src_raw)).strip() # 解码后用于下载 # 跳过已经是微信CDN的图、data URI、空src if not src or src.startswith("data:") or "mmbiz" in src: continue try: local = _ensure_local(src, html_dir) url = upload_img(token, local) html = html.replace(f'src="{src_raw}"', f'src="{url}"', 1) img_count += 1 print(f" {src[:50]} -> {url[:44]}...") except Exception as e: print(f" [warn] 图片上传失败 {src[:50]}: {e}") ``` ### Technical Analysis The local variable `html` is assigned the article body as a string. The code then calls `html.unescape(src_raw)`, but strings do not provide an `unescape` method. The intended operation was presumably `html.unescape()` from Python's standard `html` module. Consequently, processing an eligible image raises `AttributeError` before `_ensure_local()` or `upload_img()` can replace the source with a WeChat CDN URL. The broad `except Exception` handler converts the failure into a warning and permits the pipeline to continue. The subsequent validator accepts arbitrary `http://` and `https://` image sources. Therefore, a failed upload does not block publication, and the original third-party URL can remain in the draft. This contradicts the documented behavior that article images are uploaded and replaced with WeChat-hosted URLs. ### Attack Path 1. An attacker supplies or influences an HTML article containing an image such as: ```html ...[truncated 1440 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/make_preview.py:174
Finding

Offline preview files retain remote resources that contact third-party servers when opened

Content
View full analysis
]*\bsrc="([^"]+)"[^>]*>', html_body, flags=re.I): src_raw = img_m.group(1).strip() # 转义形态(匹配 html 原文) src = re.sub(r"[\x00-\x1f\x7f]+", "", html_mod.unescape(src_raw)).strip() if not src or src.startswith("data:") or src.startswith("http") or "mmbiz" in src: continue # 安全解析本地路径,防止路径遍历 safe_path = _safe_resolve_local_path(src, base_dir) if safe_path: data_uri = to_data_uri(safe_path) html_body = html_body.replace(f'src="{src_raw}"', f'src="{data_uri}"', 1) return html_body ``` The preview path is invoked by `scripts/push_draft.py:646-681`, which describes the result as having no network requests: ```python # --no-push 预览模式:在凭据检查、获取access_token和上传图片之前就拦截 # 直接调用 make_preview 生成本地HTML预览,不产生任何网络请求 if args.no_push: ... print(" 用浏览器打开即可查看排版效果,无任何网络请求。") ``` ### Technical Analysis The preview generator embeds authorized local images as Base64 data URIs, but it explicitly skips any image whose source starts with `http`. Those remote URLs remain in the generated preview file. Although the Python preview-generation process does not itself fetch those resources, opening the resulting HTML in a normal browser causes the browser to request them. Thus, the generated preview is not fully offline, despite the explicit claim that opening it produces no network requests. The Base64 operation itself is not a covert exfiltration mechanism: it is limited to regular image files under the authorized base directory and enforces extension and size checks. The risk instead comes from pre ...[truncated 1222 chars]
Remediation
View remediation
``` 6. Update documentation and terminal messages if remote resources are intentionally retained. Do not claim that opening the preview performs no network requests unless that property is technically enforced. 7. Add tests that parse generated preview HTML and assert that no `http://`, `https://`, protocol-relative, or other remote resource URLs remain in strict offline mode. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个完整的微信公众号发布工具,核心能力应包括内容格式转换、微信富文本生成、以及向公众号草稿箱推送。给出的代码片段却只是一个封面图生成脚本,使用 PIL 在本地合成或绘制封面 PNG,支持主题配色和有无插画两种模式。虽然“封面自动生成”是声明中的一个子功能,因此该代码与声明存在部分相关性,但它只覆盖了整体描述中的很小一部分,且缺失了声明中的主要功能。因此该代码块与所宣称的整体用途存在明显不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

声明强调的是“一键渲染并推送到公众号草稿箱”的发布流水线能力,还提到封面自动生成和发布前自检;但提供的代码片段实际是一个本地预览工具 make_preview.py。它主要做三件事:解析 md/html/build 输入、对 HTML 做安全消毒、把本地图片转为 data URI 后包装成完整 HTML 文件输出。代码中没有任何网络访问、公众号 API 调用、草稿箱写入、登录鉴权或发布检查逻辑,因此与声明中的核心能力存在明显差异。主题/profile 支持与 HTML 直输、自动排版预览相关部分是吻合的,但整体上该片段的实际用途更接近“离线预览生成器”,而不是“推送到公众号草稿箱的发布器”。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

描述声称这是一个可一键渲染并推送到公众号草稿箱的完整技能,且包含自动排版与 HTML 直输两种模式、主题换肤、偏好向导、封面自动生成和发布前自检。实际代码只是本地解析器 md2render.py:读取 md/txt 文件,解析少量 Markdown 子集,生成微信样式 HTML 和图片占位信息,支持通过 theme.apply_theme 应用主题,并返回 title/author/digest/cover/chars/html/images 等结果。代码中没有看到对公众号接口、网络推送、草稿箱写入、HTML 输入直通处理、封面生成、向导流程或发布前检查的实现。因此,实际行为仅覆盖了描述中的“自动排版/主题渲染”子集,而整体声明明显更宽,属于描述与代码行为不一致。

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

The documented flags '--no-validate' and '--force' explicitly allow bypassing safety checks and proceeding despite blocking validation results. In a skill that accepts HTML, local files, remote images, and network publishing, such bypasses materially increase the chance of unsafe content handling, policy evasion, or publication of malformed or dangerous payloads.

Content

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

只预览不推送(先自检 + 打印正文):

$PY .../push_draft.py --config <config.json> --md article.md --no-push

换主题(覆盖 profile):--theme mono / --theme paper

换排版规范:--profile <其他路径>;跳过自检:--no-validate;BLOCK 仍强推:--force

text

### 方式二:HTML 直输模式 —— 你/AI 已排好版,技能直接推送

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
> 进阶「Python 组件流水线」请按 `SKILL.md` 下方组件速查,用 `scripts/renderer.py` 的组件

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

The tool explicitly exposes --no-validate and --force, allowing callers to bypass or override the publication safety gate before pushing content to the official account draft box. In an agent-executed skill, such bypass flags are risky because untrusted prompts or upstream automation can suppress the intended guardrails and publish content that the validator would have blocked.

Content

Scanner excerpt · scripts/push_draft.py (reported line 25)May include surrounding context.

python
无凭据时所有命令都会打印新手引导(AppID/Secret / 认证 / IP 白名单 / errcode 对照)。

通用参数:--theme(仅 md) / --title / --author / --digest / --cover(覆盖)
          --no-push 只上传+自检,不建草稿;--no-validate 跳过自检;--force 放行 BLOCK。

凭据不在本文件内:--config 指定,或环境变量 WECHAT_PUSH_CONFIG,或当前目录 config.json。
"""

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The CLI parser makes --no-validate directly available as a normal user-facing parameter, making safety checks optional rather than mandatory. In the context of a content publishing skill, optional validation weakens the trust boundary and enables policy-violating or malformed content to be pushed if an attacker or careless caller controls tool arguments.

Content

Scanner excerpt · scripts/push_draft.py (reported line 632)May include surrounding context.

python
ap.add_argument("--no-cover-auto", action="store_true", help="缺少封面时不自动生成(直接报错)")
    ap.add_argument("--check-env", action="store_true", help="只做环境自检")
    ap.add_argument("--no-push", action="store_true", help="上传+自检后不建草稿,仅打印正文")
    ap.add_argument("--no-validate", action="store_true", help="跳过发布前自检")
    ap.add_argument("--force", action="store_true", help="自检 BLOCK 时仍继续推送")
    args = ap.parse_args()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises and instructs use of powerful capabilities including shell execution, filesystem access, environment-variable consumption, and outbound network/API access, but it declares no explicit tool scope or permission boundaries. That creates an over-privileged integration surface where an agent may invoke sensitive operations without user-visible restriction or least-privilege controls.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

In --no-push mode, the tool still forwards --build to make_preview.py, and the build pipeline elsewhere uses importlib.import_module(args.build). That means a feature described as document preview/rendering can execute arbitrary local Python module code supplied by the user, which is dangerous in agent/tooling contexts where parameters may be influenced by untrusted inputs.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/push_draft.py (reported line 674)May include surrounding context.

python
preview_out = preview_tmp.name
            preview_cmd += ["--out", preview_out]
            print("[no-push] 生成本地预览(不获取access_token,不上传图片)...")
            result = subprocess.run(preview_cmd, capture_output=True, text=True)
            if result.returncode != 0:
                print(f"[FAIL] 预览生成失败:{result.stderr}")
                raise SystemExit(1)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and multiple inline descriptions state that the renderer is specifically for WeChat-native rich text and uses Chinese-language defaults throughout, indicating a fixed Chinese/WeChat locale context without presenting any user choice or opt-in. Under the policy for natural-language violations, forced locale/language behavior should be documented as optional or justified to avoid excluding users by default.

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 · scripts/wx_api.py (reported line 12)May include surrounding context.

python
"""
import requests, json, os

API_BASE = "https://api.weixin.qq.com/cgi-bin"


def _config_path():

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

This markdown file presents the skill content exclusively in Chinese, including the title, body, and usage-oriented text, with no indication that the user can choose another language. Under the natural-language policy check, forcing a specific language without user opt-in is a policy concern unless the locale restriction is clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This file declares lang="zh-CN", indicating the content is fixed to Simplified Chinese. Under the policy rules, locale-specific language can be a violation when the skill forces a language without user opt-in and no region-specific justification is provided in the file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script sets default visible text content to English/Chinese labels, with --cn defaulting to 阅读, which creates a built-in locale assumption in generated output. There is no accompanying user opt-in, locale selection mechanism, or justification that this skill is intentionally region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and user-facing guidance are entirely in Chinese, and the script's prompts and status messages throughout the file are also Chinese-only. This creates a natural-language locale constraint without any explicit user opt-in or documented justification for restricting the interface language.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module docstring states that --no-push means '只上传+自检,不建草稿', implying remote image upload and validation still happen. In main(), the --no-push branch exits early to run make_preview.py before credential checks, token retrieval, uploads, or push-time validation, which directly contradicts the documented behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This JSON file includes user-facing text such as "desc" and "scenes" in Chinese only, while only the theme names have English variants. If these fields are surfaced directly to users, the skill effectively forces a specific language for part of the experience without opt-in or an alternative locale.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The "mono" theme contains descriptive metadata in Chinese only for fields likely intended for display to end users. This may violate language/locale policy if the skill does not explicitly let users choose their language or document a region-specific scope.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The "paper" theme likewise provides user-facing description and scene metadata only in Chinese. Without a documented locale constraint or user choice, this can amount to forcing a specific language for part of the skill output.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.