Back to skill

Security audit

微信公众号文章排版

Security checks for vulnerabilities and agentic risk

Overview

This WeChat formatter is mostly purpose-aligned, but it has review-worthy risks around broad local file discovery, unsanitized auto-opened previews, and arbitrary image fetching during publishing.

Review before installing. Use this only with trusted local articles, narrow vault_root to a specific attachment folder, avoid untrusted external image URLs, consider --no-open for previews, and understand that publishing uses WeChat credentials and sends article content and images to WeChat.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/format.py:284
Finding

Overbroad Home-Directory Search Can Copy Sensitive Local Files into Publication Artifacts

Content
View full analysis
str: """把 Obsidian ![[image.jpg]] 转为 标签,复制图片到输出目录""" images_dir = output_dir / "images" # 搜索路径:vault 目录(如需额外图片目录,在 config.json 的 image_search_paths 中配置) search_roots = [vault_root] # 支持自定义图片搜索目录 config_path = SKILL_DIR / "config.json" if config_path.exists(): import json as _json try: _cfg = _json.load(open(config_path, encoding="utf-8")) for p in _cfg.get("image_search_paths", []): search_roots.append(Path(p).expanduser()) except Exception: pass def replace_img(match): filename = match.group(1).strip() # 处理带尺寸的 wikilink: ![[image.jpg|300]] if "|" in filename: filename = filename.split("|")[0].strip() # 在多个目录中搜索图片(followlinks=True 跟随符号链接) for search_root in search_roots: if not search_root.exists(): continue for root, dirs, files in os.walk(search_root, followlinks=True): if filename in files: img_path = Path(root) / filename images_dir.mkdir(parents=True, exist_ok=True) dest = images_dir / filename if not dest.exists(): shutil.copy2(img_path, dest) # 返回占位标记,后面注入样式时处理 return f'
{filename}
' return f'[图片: {filename}]' return re.sub(r"!\[\[([^\]]+)\]\]", replace_img, text) ``` ### Technical Analysis The configured vault ...[truncated 2327 chars]
Remediation
View remediation

other

Error
Location
scripts/publish.py:160
Finding

Server-Side Request Forgery Through Unvalidated External Image URLs

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/format.py:708
Finding

Untrusted Raw HTML Executes in Automatically Opened Local Preview Pages

Content
View full analysis
str: """Markdown 转 HTML""" html = markdown.markdown( content, extensions=["tables", "fenced_code", "nl2br"], ) return html ``` ```python def generate_preview(article_html: str, footnote_html: str, theme: dict, title: str, word_count: int, output_path: Path): """生成浏览器预览 HTML 文件""" template_path = TEMPLATE_DIR / "preview.html" template = template_path.read_text(encoding="utf-8") # 合并文章和脚注 full_html = article_html if footnote_html: full_html += "\n" + footnote_html preview_html = ( template .replace("{{TITLE}}", title) .replace("{{THEME_NAME}}", theme.get("name", "")) .replace("{{WORD_COUNT}}", f"{word_count:,}") .replace("{{ARTICLE_HTML}}", full_html) ) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(preview_html, encoding="utf-8") ``` ```python if AUTO_OPEN and not args.no_open: webbrowser.open(f"file://{gallery_path}") print("已在浏览器中打开画廊") ``` ```python if AUTO_OPEN and not args.no_open: webbrowser.open(f"file://{preview_path}") print("已在浏览器中打开预览") ``` ### Technical Analysis Python-Markdown preserves raw HTML unless an independent sanitization stage is applied. The formatter does not use an HTML sanitizer or allowlist before inserting the converted article into `preview.html` and `gallery.html`. Consequently, raw `
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (39)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| `scripts/format.py` | 排版引擎:Markdown → 微信兼容 HTML |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

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

md
微信凭证优先从环境变量读取(`~/.openclaw/.env`),无需在 config.json 中配置敏感信息。

### 环境变量(自动从 ~/.openclaw/.env 加载)

| 变量 | 用途 |
|------|------|

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/publish.py (reported line 72)May include surrounding context.

python
微信凭证优先从环境变量读取(`~/.openclaw/.env`),无需在 config.json 中配置敏感信息。

### 环境变量(自动从 ~/.openclaw/.env 加载)

| 变量 | 用途 |
|------|------|

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/publish.py (reported line 40)May include surrounding context.

python
# ── 环境变量加载(OpenClaw 统一配置)──────────────────────────────────
for env_path in [
    SKILL_DIR / ".env",
    Path.home() / ".openclaw" / ".env",
    Path.home() / ".workbuddy" / ".env",
]:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/publish.py (reported line 41)May include surrounding context.

python
# ── 环境变量加载(OpenClaw 统一配置)──────────────────────────────────
for env_path in [
    SKILL_DIR / ".env",
    Path.home() / ".openclaw" / ".env",
    Path.home() / ".workbuddy" / ".env",
]:

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/publish.py (reported line 42)May include surrounding context.

python
# ── 环境变量加载(OpenClaw 统一配置)──────────────────────────────────
for env_path in [
    SKILL_DIR / ".env",
    Path.home() / ".openclaw" / ".env",
    Path.home() / ".workbuddy" / ".env",
]:

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill is configured to trigger on very broad, everyday phrases like '排版', '美化', and 'format', which can cause it to activate in contexts the user did not intend. Because this skill reads files, transforms content, opens browsers, and can lead into publishing workflows, overbroad invocation increases the chance of unintended file access or content transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill describes optional draft publishing to WeChat but does not clearly warn users that article content and associated credentials will be used to transmit data to an external platform. In a skill that handles local files and account-linked publishing, missing disclosure weakens informed consent and can lead to accidental exfiltration of private or unpublished content.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains natural-language instructions, help text, and status output exclusively in Chinese, beginning with the module docstring and continuing through CLI descriptions. Under the policy, forcing a specific language without offering a language or locale choice is a natural-language policy violation.

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/publish.py (reported line 76)May include surrounding context.

python
sys.exit(1)

    url = (
        "https://api.weixin.qq.com/cgi-bin/token"
        f"?grant_type=client_credential&appid={app_id}&secret={app_secret}"
    )
    resp = requests.get(url, timeout=15)

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/publish.py (reported line 99)May include surrounding context.

python
sys.exit(1)

    url = (
        "https://api.weixin.qq.com/cgi-bin/token"
        f"?grant_type=client_credential&appid={app_id}&secret={app_secret}"
    )
    resp = requests.get(url, timeout=15)

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/publish.py (reported line 127)May include surrounding context.

python
sys.exit(1)

    url = (
        "https://api.weixin.qq.com/cgi-bin/token"
        f"?grant_type=client_credential&appid={app_id}&secret={app_secret}"
    )
    resp = requests.get(url, timeout=15)

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/publish.py (reported line 241)May include surrounding context.

python
sys.exit(1)

    url = (
        "https://api.weixin.qq.com/cgi-bin/token"
        f"?grant_type=client_credential&appid={app_id}&secret={app_secret}"
    )
    resp = requests.get(url, timeout=15)

Tainted flow: 'files' from requests.get (line 141, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
84% confidence
Finding

External image URLs embedded in article HTML are fetched and then re-uploaded to WeChat without origin restrictions, creating an SSRF-style fetch-and-forward primitive. If an attacker can supply article HTML, they can cause the host running this tool to make outbound requests to arbitrary URLs and transmit retrieved content onward, potentially reaching internal services or sensitive endpoints.

Content

Scanner excerpt · scripts/publish.py (reported line 142)May include surrounding context.

python
try:
            with open(image_path, "rb") as f:
                files = {"media": (filename, f, content_type)}
                resp = requests.post(url, files=files, timeout=30)

            data = resp.json()
            if "url" in data:

Tainted flow: 'body' from requests.get (line 258, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/publish.py (reported line 259)May include surrounding context.

python
# 必须用 ensure_ascii=False,否则中文被转义为 \uXXXX 导致微信计算标题长度错误
    body = json.dumps(data, ensure_ascii=False).encode("utf-8")
    resp = requests.post(url, data=body,
                         headers={"Content-Type": "application/json"}, timeout=30)
    result = resp.json()

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/publish.py (reported line 350)May include surrounding context.

python
"--theme", theme,
            "--no-open",
        ]
        result = subprocess.run(format_cmd, capture_output=True, text=True)
        if result.returncode != 0:
            print(f"排版失败:\n{result.stderr}")
            sys.exit(1)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document declares lang="zh-CN", and the UI text throughout the template is exclusively Chinese, which imposes a specific language/locale on all users. Under the policy, this is a natural-language locale constraint and there is no visible opt-in, fallback, or justification that the skill is region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The HTML document hard-codes lang="zh-CN", which imposes a specific language/locale setting on all users. Under the policy, locale constraints should either be optional for the user or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This JSON theme file uses Chinese for the human-facing name and description, which can impose a specific language/locale on users without any opt-in or justification. The policy explicitly disallows language or locale constraints unless they are optional or clearly documented as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This JSON manifest contains human-readable metadata only in Chinese (the name and description), which can represent a locale-specific natural-language constraint without any indication of user opt-in or that the theme is intentionally limited to a Chinese-language context. Under the policy rule for language/locale handling, forcing a specific language without documented choice or justification is a potential policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The JSON uses Chinese-only natural-language values for the theme name and description. Because this file provides user-facing metadata and does not offer a language choice or document that the theme is intentionally region-specific, it may violate a language/locale policy requiring user opt-in or justified locale constraints.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The file name and description explicitly define the theme as Chinese-language and Chinese-culture oriented ("中国风", Chinese-style description text). For a general-purpose skill/theme catalog, this constitutes a locale/language constraint expressed in natural language without any visible user choice or justification in the file itself.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This JSON manifest contains natural-language fields for the theme name and description entirely in Chinese. Under the stated policy, forcing a specific language without user opt-in or a documented locale-specific justification is a natural-language policy issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This JSON file contains natural-language metadata entirely in Chinese for the theme name and description. Because the file does not indicate that the theme is intended only for Chinese-speaking users or offer any language/locale choice, it may violate the policy against forcing a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This JSON theme file contains user-facing natural-language metadata entirely in Chinese, and there is no indication that the theme is region-specific or that users can choose a locale. Under the policy rule for language/locale constraints, hard-coding a specific language without opt-in can be a natural-language policy concern.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.