Back to skill

Security audit

wechat-message

Security checks for vulnerabilities and agentic risk

Overview

This WeChat automation skill can read private chats, send replies, and forward chat content to a configurable AI endpoint while also claiming that no data is uploaded.

Review before installing. Use only with contacts and chats you are allowed to automate, prefer a local HTTPS or trusted AI endpoint, avoid putting API keys on the command line, delete or protect wechat_automation.log, and do not run auto_process unless you accept unattended replies being sent from your WeChat account.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wechat.py:1694
Finding
Private WeChat Messages and API Credentials Can Be Transmitted to Arbitrary Plaintext Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wechat.py:1410-1411`, `scripts/wechat.py:1453-1455`, and `scripts/wechat.py:1694-1728` **Vulnerability Type**: Unrestricted sensitive-data transmission over potentially insecure transport **Risk Level**: High ### Vulnerable Code ```python def auto_process_contacts(self, contact_list: List[str], max_polling_times: int = 10, api_url: str = "http://localhost:8000/api/chat/message", api_key: str = "") -> None: ``` ```python latest_messages = self._get_latest_messages(message_rects) if latest_messages: # Call the AI interface to obtain replies ai_replies = self._call_ai_api(latest_messages, api_url, api_key) ``` ```python def _call_ai_api(self, customer_messages: List[str], api_url: str, api_key: str) -> List[Dict[str, Any]]: try: headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}" } # Combine customer messages into one string combined_message = "\n".join(customer_messages) data = { "user_id": "7", "message": combined_message, "username": "" } self.logger.info(f"调用AI接口: {api_url}") self.logger.info( f"请求消息: {len(customer_messages)}条,合并为: " f"{combined_message[:50]}..." ) response = requests.post( api_url, json=data, headers=headers, timeout=30 ) ``` ### Technical Analysis The automatic-reply feature extracts incoming WeChat messages from the desktop interface, joins them into a single string, and submits them verbatim to `api_url`. The request also includes the configured API credential as a bearer token. The destination is entirely controlled through the `--api_url` argument. The implementation does not: - Require HTTPS for non-loopback destinations. - R ...[truncated 2256 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS for every non-loopback destination. Permit plaintext HTTP only for explicitly recognized loopback hosts such as `127.0.0.1`, `::1`, or `localhost`. 2. Parse and validate the URL before use, including its scheme, hostname, port, and resolved address. 3. Disable automatic redirects or revalidate every redirect target so an approved HTTPS destination cannot redirect to HTTP or an untrusted host. 4. Provide a configurable allowlist of trusted API origins. 5. Display the destination and a clear data-disclosure notice before the first transmission, and require explicit user consent. 6. Transmit only the minimum message context needed. Add configurable redaction for credentials, financial details, phone numbers, and other personal information. 7. Separate local-only operation from remote AI operation in both the CLI and documentation. 8. Correct the privacy documentation so it clearly states when and where chat content leaves the device. 9. Consider requiring a user confirmation step before API-generated content is sent to a WeChat contact. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wechat.py:847
Finding
Private Message Content and Complete API Responses Are Written to Persistent Logs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wechat.py:847-867`, `scripts/wechat.py:892-959`, and `scripts/wechat.py:1720-1742` **Vulnerability Type**: Sensitive information exposure through application logs **Risk Level**: Medium ### Vulnerable Code ```python def _type_with_clipboard(self, text: str): try: original_clipboard = pyperclip.paste() pyperclip.copy(text) pyautogui.hotkey('ctrl', 'v') pyperclip.copy(original_clipboard) self.logger.info(f"使用剪贴板输入: {text}") ``` ```python self.logger.info( f"开始执行:搜索联系人 '{contact_name}' 并发送消息: '{message}'" ) ... self.logger.info(f"2. 输入消息: {message}") ... self.logger.info( f"✅ 消息发送完成!联系人: {contact_name}, 消息: {message}" ) ``` ```python self.logger.info(f"调用AI接口: {api_url}") self.logger.info( f"请求消息: {len(customer_messages)}条,合并为: " f"{combined_message[:50]}..." ) response = requests.post( api_url, json=data, headers=headers, timeout=30 ) if response.status_code == 200: result = response.json() self.logger.info(f"API响应: {result}") ``` The logger is configured to create a persistent file: ```python logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('wechat_automation.log', encoding='utf-8'), logging.StreamHandler() ] ) ``` ### Technical Analysis Message text passed through `_type_with_clipboard` is logged in full. The search-and-send workflow also records contact names and complete outgoing messages. The AI workflow logs a prefix of incoming message content and serializes the entire parsed API response into the log. These records are written to `wechat_automation.log` at the default INFO level. The implementation does not apply message redaction, restrictive file permissions, retention limits, log rotation, or an explicit opt-in debug setting for content logging. Even when only the first 50 chara ...[truncated 1427 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all message bodies, contact names, and API response bodies from default INFO-level logs. 2. Log only operational metadata, such as message count, success status, elapsed time, and a non-sensitive correlation identifier. 3. If content logging is required for troubleshooting, place it behind an explicit, temporary debug option that warns the user before activation. 4. Redact tokens, authentication codes, phone numbers, email addresses, and other sensitive patterns before logging. 5. Create log files with permissions restricted to the current user. 6. Add size-based or time-based rotation and a short default retention period. 7. Provide a command or documented procedure to securely delete logs. 8. Avoid sending sensitive content to the console because terminal output may also be captured by shell transcripts, CI systems, or desktop logging tools. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wechat.py:1767
Finding
API Credentials Are Accepted Through Command-Line Arguments and Partially Printed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wechat.py:1767-1768` and `scripts/wechat.py:1848-1858` **Vulnerability Type**: Credential exposure through process arguments, shell history, and console output **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument( '--api_url', help='AI接口URL(用于auto_process操作)' ) parser.add_argument( '--api_key', required='auto_process' in sys.argv, help='API密钥(auto_process操作必需)' ) ``` ```python print(f"AI接口: {args.api_url or 'http://localhost:8000/api/chat/message'}") print(f"API密钥: {args.api_key[:8]}...") wechat.auto_process_contacts( contact_list=contact_list, max_polling_times=args.polling_times or 10, api_url=args.api_url or "http://localhost:8000/api/chat/message", api_key=args.api_key ) ``` The documentation encourages the same insecure invocation pattern: ```bash py wechat.py auto_process --api_key "你的AI接口密钥" ``` ### Technical Analysis The implementation requires the API key as a command-line argument for `auto_process`. Command-line secrets can be exposed through: - Shell history files. - Process inspection utilities available to other local users or processes. - Terminal session recording. - Automation logs and command wrappers. - Diagnostic reports that capture process arguments. Although `skill.md` describes a `CHAT_API_KEY` environment variable, the audited implementation does not read it. The program additionally prints the first eight characters of the supplied key. A partial token can help identify, correlate, or distinguish credentials and is unnecessary for normal operation. ### Attack Path 1. The user follows the documented example and enters the API key with `--api_key`. 2. The shell records the complete invocation in its command history. 3. While the process runs, the key may also be visible in process command-line metadata. 4. The program prints an eight-character prefix to the terminal. 5. A local user or process reads shell his ...[truncated 675 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read the credential from `CHAT_API_KEY` as documented, rather than requiring it on the command line. 2. Prefer an operating-system credential manager or protected configuration file over environment variables for long-lived secrets. 3. Support secure interactive entry with `getpass.getpass()` when no stored credential is available. 4. Deprecate `--api_key` and emit a warning without echoing its value if backward compatibility is temporarily required. 5. Remove the statement that prints the token prefix. 6. Update every usage example so secrets are never embedded in command lines. 7. Recommend short-lived, narrowly scoped tokens and provide token rotation and revocation guidance. ]]>

T08 · Insecure Dependencies

Note
Location
skill.md:20
Finding
Third-Party Dependencies Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `skill.md:20-29` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```bash # Core dependencies pip install pyautogui pillow opencv-python numpy pyperclip requests psutil # Additional Windows dependencies pip install pywin32 # Additional macOS dependencies pip install pyobjc-framework-Quartz pyobjc-core pyobjc ``` ### Technical Analysis The installation instructions retrieve packages by name without exact versions, hashes, or a lock file. Consequently, two installations performed at different times may receive materially different code. The listed package names are consistent with imports observed in the implementation, and the audit found no suspicious alternate package index or apparent typosquatted name. The risk arises from mutable upstream releases and the absence of integrity verification rather than evidence that a currently listed package is malicious. This concern is elevated by the permissions required by the Skill. Dependencies may execute in a process that can control the mouse and keyboard, read and replace clipboard contents, capture WeChat screen regions, and access the network. ### Attack Path 1. An upstream package release, maintainer account, or package-distribution channel is compromised, or a future release introduces malicious behavior. 2. A user follows the documented unpinned `pip install` commands. 3. `pip` resolves and installs the then-current package release without checking a project-supplied hash. 4. Malicious package installation or import-time code executes under the user's account. 5. The compromised dependency can potentially access the same desktop automation, clipboard, screen-capture, filesystem, and network capabilities available to the Skill. ### Impact Assessment Successful dependency compromise could execute code with the privileges of the user running the Skill. Given the declared macOS accessibilit ...[truncated 406 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed requirements or lock file containing exact dependency versions. 2. Generate and verify cryptographic hashes for all distributions. 3. Install with `pip install --require-hashes -r requirements.txt`. 4. Separate Windows and macOS dependency sets while pinning each platform's transitive dependencies. 5. Use an isolated virtual environment and avoid installing the Skill as an administrator or root user. 6. Periodically scan pinned dependencies for known vulnerabilities and perform controlled, reviewed upgrades. 7. Where feasible, use a trusted internal package mirror and restrict package-index configuration. ]]>
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)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
startupinfo.wShowWindow = 1
                    CREATE_NO_WINDOW = 0x08000000

                    process = subprocess.Popen(
                        [wechat_path],
                        startupinfo=startupinfo,
                        shell=True,
Confidence
90% confidence
Finding
This duplicate finding points to the same risky launch site: subprocess.Popen(..., shell=True). The issue is the unnecessary shell mediation around process creation, which can turn environment/path manipulation into command execution opportunities.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
startupinfo.wShowWindow = 1
                    CREATE_NO_WINDOW = 0x08000000

                    process = subprocess.Popen(
                        [wechat_path],
                        startupinfo=startupinfo,
                        shell=True,
Confidence
90% confidence
Finding
This duplicate finding points to the same risky launch site: subprocess.Popen(..., shell=True). The issue is the unnecessary shell mediation around process creation, which can turn environment/path manipulation into command execution opportunities.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code implements automated extraction of message content and external/API-driven reply behavior, which is not obviously necessary for simple local UI automation. In context, this broadens the capability from desktop control to autonomous surveillance and relay of private chat data.

Ssd 3

High
Confidence
98% confidence
Finding
The automatic contact-processing loop collects recent messages, sends them to another system for response generation, and then sends replies autonomously. This creates an end-to-end pipeline for surveillance and automated action on private chats, which is especially risky in a messaging automation skill.

Missing User Warnings

High
Confidence
99% confidence
Finding
Customer messages are sent to an external AI API without a clear warning about data sharing, destination, retention, or sensitivity. Because the data consists of private chat content, silent transmission creates a substantial confidentiality and compliance risk.

Ssd 3

High
Confidence
99% confidence
Finding
The workflow automatically packages chat content and forwards it to an external AI service as part of normal operation. In this skill context, that is a direct sensitive-data disclosure path involving personal communications, making the behavior materially dangerous.

Missing User Warnings

High
Confidence
99% confidence
Finding
The skill claims no data is uploaded while documenting AI API-based automatic replies, which strongly implies that message contents may be sent off-host for processing. In a WeChat automation context, this is especially sensitive because private conversations, business messages, and personal identifiers could be disclosed to external services without users understanding the risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 尝试在PATH中查找
        if IS_WINDOWS:
            try:
                result = subprocess.run(['where', 'WeChat.exe'],
                                        capture_output=True, text=True, encoding='gbk',
                                        creationflags=subprocess.CREATE_NO_WINDOW)
                if result.stdout:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
startupinfo.wShowWindow = 1
                    CREATE_NO_WINDOW = 0x08000000

                    process = subprocess.Popen(
                        [wechat_path],
                        startupinfo=startupinfo,
                        shell=True,
Confidence
88% confidence
Finding
The code launches WeChat with subprocess.Popen([wechat_path], shell=True). Even though it passes a list, enabling shell=True unnecessarily routes execution through the shell and increases the risk of path/parameter interpretation issues if the discovered executable path is ever influenced by environment or filesystem manipulation. In a desktop automation skill that already manipulates a privileged GUI session, spawning processes through the shell is more dangerous than necessary.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# macOS启动
                self.logger.info("macOS启动微信")
                try:
                    subprocess.run(['open', '-a', 'WeChat'],
                                   capture_output=True, text=True, timeout=10)
                    self.logger.info("使用open命令启动微信")
                except Exception as e:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
except Exception as e:
                    self.logger.warning(f"open命令失败,尝试其他方法: {e}")
                    try:
                        subprocess.run(['open', '-a', '微信'],
                                       capture_output=True, text=True, timeout=10)
                        self.logger.info("使用中文名启动微信")
                    except Exception as e2:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The function comment says it returns only the other party's messages, but the implementation includes both 'me' and 'you' sides. This kind of privacy-affecting mismatch can cause downstream consumers to over-collect or disclose data under a false assumption of limited scope.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill copies chat history from the GUI into the system clipboard and reads it programmatically without a clear user-facing warning or consent flow. Clipboard-based access can expose sensitive message contents to other local processes and surprises users because private chat data is being harvested through desktop automation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill performs automated polling and message sending in an infinite loop with limited runtime transparency. In a messaging context this can lead to unintended autonomous actions, spam, disclosure, or user-account misuse if enabled without clear ongoing notice and operator control.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The skill is presented as a local desktop WeChat automation tool, but it also forwards collected chat messages to a configurable AI API for reply generation. This mismatch is security-relevant because users may reasonably expect local-only processing while private communications are actually transmitted off-host.

External Transmission

Medium
Category
Data Exfiltration
Content
self.logger.info(f"调用AI接口: {api_url}")
            self.logger.info(f"请求消息: {len(customer_messages)}条,合并为: {combined_message[:50]}...")

            response = requests.post(
                api_url,
                json=data,
                headers=headers,
Confidence
99% confidence
Finding
requests.post(api_url, json=data, ...) transmits combined customer messages to a configurable external endpoint. Because the payload contains private chat content and the destination can be changed, this is a concrete exfiltration mechanism rather than merely a theoretical concern.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The document states that all operations are local and no data is uploaded, but other sections explicitly describe sending content to an external AI API for automatic replies. This can mislead users into exposing chat contents, contact data, or metadata to a network service without informed consent or proper security review.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
try:
            config = {}
            for field_name in self.region.__dataclass_fields__:
                value = getattr(self.region, field_name)
                if isinstance(value, tuple):
                    config[field_name] = list(value)
                else:
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
Most of the skill is written in Chinese, but the authentication section switches to English imperative instructions such as 'All requests require' and 'Environment Variable' with no opt-in or explanation. This can constitute a language-policy issue because the skill imposes mixed-language instructions on the user without documenting the requirement or offering a choice.