Back to skill

Security audit

Wechat Oa

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real WeChat publishing tool, but relay and hybrid modes can send account secrets and unpublished content through a third-party server.

Review this skill before installing. Prefer direct mode only, avoid relay/hybrid with production accounts unless you fully trust and control the relay, rotate any AppSecret or relay key previously used with relay mode, and be careful with delete commands because the CLI does not prompt for confirmation.

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

other

Error
Location
wechat_oa/api/draft.py:36
Finding

WeChat credentials and private publishing data are disclosed to a third-party relay

Content
View full analysis
Dict: import base64 import os from wechat_oa.core.config import get_account from wechat_oa.features.cover_generator import generate_cover cfg = get_account(account_name) json_data = { "appid": cfg["APP_ID"], "appsecret": cfg["APP_SECRET"], "title": title, "content": content, "author": author or cfg.get("author", ""), "digest": digest, } if thumb_media_id: json_data["thumb_media_id"] = thumb_media_id else: cover_path = generate_cover(title) if os.path.exists(cover_path): with open(cover_path, 'rb') as f: img_data = f.read() json_data["thumb_image"] = base64.b64encode(img_data).decode('utf-8') json_data["thumb_filename"] = os.path.basename(cover_path) from .relay import relay_post result = relay_post("push/article", json_data, cfg) ``` Material upload places the credentials in the request URL and uploads the selected file: ```python def _material_upload_relay(file_path: str, media_type: str = "image") -> Dict: from wechat_oa.core.config import load_config cfg = load_config() result ...[truncated 4878 chars]
Remediation
View remediation

other

Error
Location
wechat_oa/api/router.py:5
Finding

Hybrid fallback can silently redirect credentials and publishing data to the third-party relay

Content
View full analysis
bool: err_msg = str(e).lower() ip_keywords = ["ip", "whitelist", "白名单", "40164", "40013", "invalid ip"] return any(kw in err_msg for kw in ip_keywords) ``` ```python def hybrid_route(direct_func: Callable, relay_func: Callable) -> Callable: def decorator(func: Callable) -> Callable: @wraps(func) def wrapper(*args, **kwargs): from wechat_oa.core.config import load_config cfg = load_config() push_mode = cfg.get("PUSH_MODE", "direct") if push_mode == "hybrid": try: return direct_func(*args, **kwargs) except Exception as e: if _is_ip_whitelist_error(e): return relay_func(*args, **kwargs) raise if push_mode == "relay": return relay_func(*args, **kwargs) return direct_func(*args, **kwargs) return wrapper return decorator ``` ### Technical Analysis Hybrid mode catches every `Exception` raised by a direct operation and classifies the failure by searching its string representation for generic substrings. The token `"ip"` is especially broad and is not tied to a structured WeChat response or an exact error code. If any exception string happens to contain one of these substrings, the router invokes the relay implementation automatically. Relay implementations send the AppSecret and operation data to a third party. Consequently, an operation that begins as a direct request to the official WeChat API can change its trust boundary without transaction-s ...[truncated 1402 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
wechat_oa/core/token.py:65
Finding

WeChat access tokens are cached in plaintext without restrictive filesystem permissions

Content
View full analysis
bool: if not self.cache_file.exists(): return False try: with open(self.cache_file, "r", encoding="utf-8") as f: cache = json.load(f) if cache.get("expires_at", 0) > time.time() + 300: self._token = cache["access_token"] self._expires_at = cache["expires_at"] return True except Exception: pass return False ``` ### Technical Analysis The bearer access token is written as plaintext JSON under the project `.cache` directory. The code relies on the process umask and existing directory permissions; it does not explicitly create the directory as owner-only or create the token file with mode `0600`. On a multi-user system, shared workspace, permissively mounted project directory, or environment with a weak umask, another local user or process may be able to read the cache. Because the token is a bearer credential, possession is sufficient to use it until it expires. The filename contains only a truncated hash of the AppID. That naming mechanism does not encrypt or protect the token contents. ### Attack Path 1. The Skill obtains a WeChat access token and invokes `_save_to_cache()`. 2. The token and expiration time are written to `.cache/access_token_.json`. 3. Filesystem permissions inherited from the environment permit another local principal or process to read the ...[truncated 592 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (52)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The README recommends relay/hybrid modes using a public intermediary server and even provides a hard-coded HTTP endpoint, but does not clearly warn that article content, account-related API traffic, and relay credentials may transit a third party. In this skill context, the tool manages WeChat OA drafts and materials, so sending unpublished content and operational secrets through an unaudited relay materially increases the risk of interception, content leakage, credential abuse, and account compromise.

Content

No source excerpt is available for this finding.

Hidden Instructions

High
Category
Prompt Injection
Confidence
78% confidence
Finding

The file begins with an invisible BOM/hidden character and front-matter directives including alwaysApply: true, which can act as hidden control instructions that are not obvious to reviewers. Hidden or non-obvious instructions are risky in agent skills because they can silently alter model behavior, reduce transparency, and make auditing harder even when the visible content appears benign.

Content

Scanner excerpt · design.md (reported line 1)May include surrounding context.

md
---
description: HTML代码生成器严格工作规范
alwaysApply: true
---

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

This function deletes remote material identified by media_id and returns success immediately, but there is no confirmation prompt, cautionary message, or explanatory comment indicating that the action is destructive. Deletion is irreversible enough to warrant explicit disclosure before execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The relay deletion path both forwards application credentials and triggers a destructive delete through an intermediary service. This is more dangerous than the direct wrapper because compromise or misuse of the relay can lead to unauthorized deletion of materials while also exposing the APP_SECRET.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · wechat_oa/core/config.py (reported line 106)May include surrounding context.

python
except Exception as e:
            raise ConfigError(f"加载配置文件失败: {e}")
    
    env_config = {k: v for k, v in os.environ.items() if k.startswith("WECHAT_OA_")}
    for key, value in env_config.items():
        config_key = key.replace("WECHAT_OA_", "")
        if config_key in config:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The markdown advertises draft and material deletion features, and later shows wechat-oa delete and wechat-oa del-material usage, but does not warn users that these actions may permanently remove content or require confirmation. For markdown files, SQP-2 applies when the skill description omits warnings about behaviors that could affect user data or system integrity.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases include broad natural-language requests such as viewing drafts, creating drafts, pushing articles, uploading images, and generating summaries, any of which could plausibly appear in ordinary conversation or unrelated context. Because this skill performs real publishing and content-management actions against a WeChat Official Account, accidental invocation could lead to unauthorized content changes, uploads, or deletions rather than a harmless response.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The template hard-codes lang="zh-CN", which imposes a specific language/locale in the generated output. The document does not indicate that this is optional, user-selected, or limited to a clearly justified region-specific compliance context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest description and several keywords are presented in Chinese, strongly signaling a Chinese-only interaction model, but there is no indication that users may choose another language or that the locale restriction is intentional and justified. Per the policy, forcing a specific language without opt-in can be 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 · references/wechat_api.md (reported line 8)May include surrounding context.

md
import json
from typing import Dict, Optional

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


def _get(token_manager):

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

md
import json
from typing import Dict, Optional

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


def _get(token_manager):

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

md
import json
from typing import Dict, Optional

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


def _get(token_manager):

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

md
import json
from typing import Dict, Optional

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


def _get(token_manager):

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

md
import json
from typing import Dict, Optional

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


def _get(token_manager):

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 · wechat_oa/api/direct.py (reported line 5)May include surrounding context.

python
import json
from typing import Dict, Optional

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


def _get(token_manager):

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 · wechat_oa/core/token.py (reported line 8)May include surrounding context.

python
import json
from typing import Dict, Optional

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


def _get(token_manager):

Tainted flow: 'data' from requests.post (line 54, 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 · wechat_oa/api/direct.py (reported line 33)May include surrounding context.

python
headers = {"Content-Type": "application/json; charset=utf-8"}
        data = json.dumps(json_data, ensure_ascii=False).encode("utf-8")
        resp = requests.post(url_with_token, data=data, headers=headers, timeout=30)
        resp.encoding = "utf-8"
        data = resp.json()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code reads a local file and uploads it to an external WeChat API endpoint, which is a safety-relevant operation affecting user data. There is no confirmation prompt, user-visible log/print, or inline warning/comment disclosing that local file contents will be transmitted off-system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The relay path explicitly includes APP_ID and APP_SECRET in request payloads sent to another component/service via relay_post/relay_put/relay_get/relay_delete. Forwarding long-lived application secrets beyond the local trust boundary materially increases credential exposure risk through logging, interception, downstream compromise, or misuse by the relay service. In a library for content/draft operations, this is more dangerous because routine publishing actions unnecessarily propagate primary credentials during normal use.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The direct and relay delete paths perform an irreversible delete operation on a draft, but this file provides no confirmation prompt, warning comment, or docstring describing that the call deletes remote content. Because deletion is destructive, the absence of any disclosure in the code is a quality and safety concern.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The relay upload path transmits both APP_ID/APP_SECRET and uploaded file content to a relay service, expanding trust from WeChat to an additional remote endpoint. If the relay is compromised, misconfigured, or not strictly necessary, it can expose long-lived credentials and sensitive file contents without any explicit consent or strong guardrails in this code.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This relay count request sends application credentials to another service even though the operation is read-only. Credential forwarding to a relay unnecessarily increases exposure of APP_SECRET and creates a second place where secrets can be intercepted, logged, or abused.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The relay material-list function sends APP_ID and APP_SECRET to a remote service to enumerate account materials. While not destructive by itself, exposing credentials to an intermediary increases the blast radius of compromise and may permit broader unauthorized access if the relay or logs are exposed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The relay_post function serializes caller-provided JSON and sends it to a configured remote server, and similar behavior appears in relay_get, relay_put, and relay_delete. The code lacks any print/log statement, docstring, comment, or confirmation indicating that user or system data may be transmitted off-host.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code opens a local file and uploads it via an HTTP POST request, which can expose user data or sensitive file contents. There is no confirmation prompt, logging statement, docstring, or inline comment warning that local files will be transmitted to a remote endpoint.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.