Back to skill

Security audit

wechat-md-publish

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for WeChat publishing, but it needs review because it can publish public content and stores WeChat credentials/tokens without enforced file protections.

Install only if you are comfortable giving the skill access to your WeChat Official Account publishing credentials and article assets. Create drafts first, use --publish only after explicit review, protect ~/.wechat_publish with owner-only permissions, avoid shared machines, and install dependencies in a dedicated virtual environment from trusted indexes.

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

Warning
Location
scripts/wx_publish.py:35
Finding

WeChat credentials and access tokens are stored without enforced restrictive permissions

Content
View full analysis
~/.wechat_publish/config.json << 'EOF' { "app_id": "你的AppID", "app_secret": "你的AppSecret" } EOF ``` ```python CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".wechat_publish") TOKEN_CACHE_FILE = os.path.join(CONFIG_DIR, "token_cache.json") ``` ```python def load_config(): """从 ~/.wechat_publish/config.json 加载 app_id 和 app_secret""" cfg_path = os.path.join(CONFIG_DIR, "config.json") if not os.path.exists(cfg_path): print(f"配置文件不存在: {cfg_path}") print("请先创建配置文件,内容如下:") print(json.dumps({"app_id": "你的AppID", "app_secret": "你的AppSecret"}, indent=2, ensure_ascii=False)) sys.exit(1) with open(cfg_path, "r", encoding="utf-8") as f: return json.load(f) ``` ```python if not force_refresh and os.path.exists(TOKEN_CACHE_FILE): with open(TOKEN_CACHE_FILE, "r", encoding="utf-8") as f: cache = json.load(f) ``` ```python os.makedirs(CONFIG_DIR, exist_ok=True) with open(TOKEN_CACHE_FILE, "w", encoding="utf-8") as f: json.dump({"time": time.time(), "token": token}, f) ``` ### Technical Analysis The documented setup stores the WeChat AppSecret in a plaintext JSON file. The script also caches the reusable WeChat access token in plaintext. Neither the setup instructions nor the implementation explicitly restricts the configuration directory to mode `0700` or credential files to mode `0600`. Consequently, actual access restrictions depend on the user's umask, pre-existing directory permissions, filesystem configuration, and backup practices. On a shared host or a system where the home directory is accessible to other accounts, another local user or compromised process may be able to read these ...[truncated 1380 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wx_publish.py:70
Finding

WeChat AppSecret and access tokens are embedded in request URLs

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:49
Finding

Third-party Python and browser dependencies are installed without version or integrity pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Lp1

High
Category
MCP Least Privilege
Confidence
90% confidence
Finding

This skill is explicitly designed to transmit content, images, and publication requests over the network, yet the provided metadata does not show network permission coverage. In a capability-restricted agent system, missing permission declarations are a real security issue because users and policy engines may not understand that invoking the skill sends data to third-party infrastructure.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
90% confidence
Finding

This skill is explicitly designed to transmit content, images, and publication requests over the network, yet the provided metadata does not show network permission coverage. In a capability-restricted agent system, missing permission declarations are a real security issue because users and policy engines may not understand that invoking the skill sends data to third-party infrastructure.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

3. 创建配置文件

bash
mkdir -p ~/.wechat_publish
cat > ~/.wechat_publish/config.json << 'EOF'
{
  "app_id": "你的AppID",

Tainted flow: 'url' from requests.get (line 350, network input) → requests.get (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/wx_publish.py (reported line 71)May include surrounding context.

python
# 请求新 token
    url = f"{API_BASE}/cgi-bin/token?grant_type=client_credential&appid={app_id}&secret={app_secret}"
    resp = requests.get(url, timeout=10)
    data = resp.json()

    if "access_token" not in data:

Tainted flow: 'url' from requests.get (line 350, 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/wx_publish.py (reported line 98)May include surrounding context.

python
token = get_access_token()
    url = f"{API_BASE}/cgi-bin/material/add_material?access_token={token}&type=image"
    with open(image_path, "rb") as f:
        resp = requests.post(url, files={"media": f}, timeout=30)
    data = resp.json()
    if "media_id" not in data:
        print(f"上传封面失败: {data}")

Tainted flow: 'url' from requests.get (line 350, 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/wx_publish.py (reported line 111)May include surrounding context.

python
token = get_access_token()
    url = f"{API_BASE}/cgi-bin/material/add_material?access_token={token}&type=image"
    with open(image_path, "rb") as f:
        resp = requests.post(url, files={"media": f}, timeout=30)
    data = resp.json()
    if "media_id" not in data:
        print(f"上传封面失败: {data}")

Tainted flow: 'url' from requests.get (line 350, 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/wx_publish.py (reported line 336)May include surrounding context.

python
}
        ]
    }
    resp = requests.post(
        url,
        data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
        timeout=30,

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

This code can transmit publication commands and associated article identifiers to an external service, resulting in real-world external disclosure and public posting. In this skill's context, external transmission is expected, but it is still security-relevant because user content may be sensitive and publication may be irreversible once triggered.

Content

Scanner excerpt · scripts/wx_publish.py (reported line 352)May include surrounding context.

python
"""发布草稿,返回 publish_id"""
    url = f"{API_BASE}/cgi-bin/freepublish/submit?access_token={token}"
    payload = {"media_id": media_id}
    resp = requests.post(url, json=payload, timeout=30)
    data = resp.json()
    if data.get("errcode", 0) != 0:
        print(f"发布失败: {data}")

Tainted flow: 'url' from requests.get (line 350, 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/wx_publish.py (reported line 352)May include surrounding context.

python
"""发布草稿,返回 publish_id"""
    url = f"{API_BASE}/cgi-bin/freepublish/submit?access_token={token}"
    payload = {"media_id": media_id}
    resp = requests.post(url, json=payload, timeout=30)
    data = resp.json()
    if data.get("errcode", 0) != 0:
        print(f"发布失败: {data}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The --publish flag causes immediate publication after draft creation without a second confirmation step at the point of execution. In an agent skill context, where parameters may be assembled automatically from natural-language intent, this raises the risk of accidental irreversible posting of sensitive, incorrect, or unreviewed content to a public channel.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

Natural-language policy issues apply to all file types, including code docstrings and CLI text. Here, the top-level description and subsequent user-facing help/output strings are presented only in Chinese, with no indication that users may select another language or locale.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.