Back to skill

Security audit

wechat-article-full-workflow

Security checks for vulnerabilities and agentic risk

Overview

The skill broadly matches a WeChat article workflow, but one bundled publishing script can delete existing WeChat drafts without clear user-controlled confirmation.

Review this before installing if you use a real WeChat Official Account. Do not run scripts/push_draft.py against production credentials unless the automatic draft-deletion behavior is removed or guarded by explicit confirmation. Treat the included credential guide and AppID as sensitive operational material, and keep secrets in a proper secret store rather than copying them through shell commands.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/push_draft.py:83
Finding

Unconditional deletion of existing WeChat drafts without confirmation

Content
View full analysis

Vulnerability Details

File Location: scripts/push_draft.py, lines 83–109 and 202–203
Vulnerability Type: Unconfirmed destructive operation
Risk Level: High

Vulnerable Code

python
def delete_all_drafts(token):
    """Delete all existing drafts."""
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/draft/batchget",
        json={"offset": 0, "count": 20, "no_content": 1},
        params={"access_token": token},
        timeout=30
    )
    data = r.json()
    items = data.get("item", [])
    print(f"[INFO] Existing drafts: {len(items)}")

    for item in items:
        mid = item["media_id"]
        title = ""
        try:
            title = item["content"]["news_item"][0]["title"]
        except Exception:
            pass
        try:
            requests.post(
                f"https://api.weixin.qq.com/cgi-bin/draft/delete",
                json={"media_id": mid},
                params={"access_token": token},
                timeout=30
            )
            print(f"  [DELETED] {mid} ({title})")
        except Exception as e:
            print(f"  [SKIP] {mid}: {e}")

The deletion is invoked unconditionally from the main workflow:

python
# Delete old drafts
delete_all_drafts(token)

Technical Analysis

Every successful invocation of push_draft.py with valid WeChat credentials calls delete_all_drafts() before uploading the requested cover image and creating the new draft. The function retrieves up to 20 existing drafts and sends a deletion request for every returned media ID.

There is no opt-in flag, dry-run mode, interactive confirmation, draft allowlist, or selection of drafts belonging to the current operation. The Skill documentation describes adding an article to the draft box and reserving final publication for user confirmation, but it does not disclose that unrelated existing drafts will be deleted.

T ...[truncated 1440 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove automatic deletion from the normal draft-upload path.
  • If cleanup is required, expose it through a separate explicit option such as --delete-draft ID; do not use a broad --delete-all default.
  • List the exact draft IDs and titles that would be affected and require explicit user confirmation before deletion.
  • Add a dry-run mode that performs enumeration without mutation.
  • Restrict deletion to draft IDs specifically selected by the user or demonstrably created by the same operation.
  • Create and verify the new draft before offering cleanup of old drafts.
  • Check the HTTP status and API response for each deletion rather than treating every completed request as successful.
  • Consider preserving metadata or backups when the service supports recovery or export.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (53)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

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

python
def get_access_token(app_id, app_secret):
    """Get stable access token."""
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/stable_token",
        json={"grant_type": "client_credential", "appid": app_id, "secret": app_secret},
        timeout=30

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

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

python
def delete_all_drafts(token):
    """Delete all existing drafts."""
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/draft/batchget",
        json={"offset": 0, "count": 20, "no_content": 1},
        params={"access_token": token},

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

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

python
except Exception:
            pass
        try:
            requests.post(
                f"https://api.weixin.qq.com/cgi-bin/draft/delete",
                json={"media_id": mid},
                params={"access_token": token},

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

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

python
print(f"[INFO] Cover: {cover_path} ({os.path.getsize(cover_path)} bytes)")

    with open(cover_path, 'rb') as f:
        r = requests.post(
            f"https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={token}&type=image",
            files={'media': (fname, f, 'image/png')},
            timeout=120

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

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

python
"only_fans_can_comment": 0,
    }]

    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/draft/add",
        json={"articles": articles},
        params={"access_token": token},

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/upload_to_wechat.py (reported line 62)May include surrounding context.

python
def get_access_token(app_id, app_secret):
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/stable_token",
        json={"grant_type": "client_credential", "appid": app_id, "secret": app_secret},
        timeout=30

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/upload_to_wechat.py (reported line 81)May include surrounding context.

python
size = os.path.getsize(filepath)

    with open(filepath, 'rb') as f:
        r = requests.post(
            f"https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={token}&type=image",
            files={'media': (fname, f, 'image/png')},
            timeout=120

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

If the implementation is really a local batch uploader/HTML rewriter that uses credentials and network uploads, then claiming a full article-production workflow materially misrepresents the skill's operational scope. That concealment increases the chance of unsafe execution, unintended data transfer, and misuse of privileged publishing credentials in contexts where users expected only content-assistance behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If the implementation is really a local batch uploader/HTML rewriter that uses credentials and network uploads, then claiming a full article-production workflow materially misrepresents the skill's operational scope. That concealment increases the chance of unsafe execution, unintended data transfer, and misuse of privileged publishing credentials in contexts where users expected only content-assistance behavior.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| 文档与理论文本 | 本 `SKILL.md`、`README.md`、其中「理论依据」段与一切理论表述 | **不在 MIT 覆盖范围内**:保留所有权利(All rights reserved) |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| 文档与理论文本 | 本 `SKILL.md`、`README.md`、其中「理论依据」段与一切理论表述 | **不在 MIT 覆盖范围内**:保留所有权利(All rights reserved) |

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · assets/material_index.json (reported line 3)May include surrounding context.

json
{
  "meta": {
    "description": "WeChat material library index — check this before generating new images",
    "last_update": "2026-05-30",
    "total_images": 7
  },
  "images": [
    {
      "id": "IMG_001",
      "description": "AI数字员工概念图 - 温暖AI形象与人协作,现代简约风格",
      "url": "https://mmbiz.qpic.cn/mmbiz_png/ak2P49AlJ3icGscTic8qjeLl7sdLcFpfsXYZfLlC9hicMCXYe0881fSf4SU7THhBAicH3zz31CsJPMu06JfshF3X80e18OfBibbxKRJArpIXIH2s/0?wx_fmt=png",
      "used_in": [
        "AI打工我躺平-20260530"
      ],
      "tags": [
        "AI助手",
        "概念图

Lp1

High
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The script invokes PowerShell subprocesses, which introduces shell execution capability beyond simple draft publishing. Even with fixed commands, undeclared shell access increases the attack surface and weakens user trust boundaries.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The script invokes PowerShell subprocesses, which introduces shell execution capability beyond simple draft publishing. Even with fixed commands, undeclared shell access increases the attack surface and weakens user trust boundaries.

Content

No source excerpt is available for this finding.

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

python
def get_access_token(app_id, app_secret):
    """Get stable access token."""
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/stable_token",
        json={"grant_type": "client_credential", "appid": app_id, "secret": app_secret},

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

python
def get_access_token(app_id, app_secret):
    """Get stable access token."""
    r = requests.post(
        "https://api.weixin.qq.com/cgi-bin/stable_token",
        json={"grant_type": "client_credential", "appid": app_id, "secret": app_secret},

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Broad trigger keywords like 'wechat', 'article', 'full', and 'workflow' can cause the skill to activate in many loosely related conversations, increasing the chance that publishing-related or file-modifying behavior is invoked outside the user's actual intent. In a skill with possible credentialed API actions, overbroad activation meaningfully raises the risk of accidental use and confused-deputy behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest description lists triggers such as "公众号文章", "写公众号", and "文章排版", which are broad topic phrases rather than narrowly scoped invocation commands. The description does not provide exclusion conditions or negative examples, so it is unclear when ordinary discussion about these topics should or should not activate the skill.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file is not a neutral article-writing aid: it hardcodes cryptocurrency framing, urges readers to act on positions, and repeatedly drives users into a private group for 'insider' updates. This materially diverges from the declared general WeChat article workflow and can be used to facilitate speculative financial promotion, off-platform funneling, and potentially misleading investment-oriented content.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The file’s title and all style guidance are written exclusively in Chinese and define a specific Chinese-language tone and phrasing, such as required colloquial expressions and audience address. There is no indication that the user can opt into another language or that this locale restriction is required for a region-specific purpose.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The entire skill content is written as a Chinese-only title template library, with no indication that users may choose another language or that the locale restriction is intentional for a region-specific use case. This creates a natural-language policy concern because it implicitly enforces a specific language without opt-in.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The guide instructs operators to use PowerShell to read a user-scoped secret from the Windows environment and bridge it into Bash. While this is not code execution by itself, it normalizes shell-based secret retrieval inside a content-production skill and expands the skill into credential access workflows unrelated to article writing. In the context of an agent skill, embedding secret-access instructions increases the chance that tooling or users will expose sensitive values during execution, logging, or prompt injection scenarios.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The document discloses a real AppID and provides operational guidance for locating the corresponding AppSecret from user environment storage. This meaningfully facilitates credential handling and potential misuse of a production integration, which is outside the stated article-production purpose of the skill. Even if the secret itself is not printed, documenting where to fetch it and tying it to a real identifier lowers the barrier to unauthorized access or accidental leakage.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The docstring downplays a destructive behavior as a benign anti-pile-up step, while the code actually deletes all drafts unconditionally. Misleading framing is dangerous because it undermines informed consent and may cause operators to run the script without realizing the data-loss impact.

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 56)May include surrounding context.

python
)
            app_secret = ps.stdout.strip() if ps.stdout.strip() else None
            if not app_id:
                ps2 = subprocess.run(
                    ['powershell', '-Command',
                     '[Environment]::GetEnvironmentVariable("WECHAT_APP_ID","User")'],
                    capture_output=True, text=True, timeout=10

Static analysis

No suspicious patterns detected.