T09 · Insecure Skill Coding Practices
- Location
scripts/wxapi.py:228- Finding
Bearer Token and Sensitive WeChat Data Can Be Transmitted to an Arbitrary Plaintext Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This WeChat automation skill is not clearly malicious, but it needs review because it can access private chats, send messages, and automatically run an unverified local service while transmitting tokens over configurable plaintext HTTP.
Install only if you trust the REST service directory and understand that the skill can read private WeChat messages, send messages, list contacts/groups, and keep a helper service running. Use a strong non-default token, avoid passing tokens on the command line, keep the API bound to loopback, do not use remote HTTP endpoints, and review the cloned service code before allowing automatic startup.
scripts/wxapi.py:228Bearer Token and Sensitive WeChat Data Can Be Transmitted to an Arbitrary Plaintext Endpoint
scripts/wxapi.py:545Authentication Token Is Disclosed Through Help Output and Command-Line Arguments
scripts/wxapi.py:22Predictable Default Bearer Token Permits Weak Authentication
scripts/wxapi.py:34Unpinned Third-Party Components and Automatic Execution of Unverified Service Code
The service endpoint can be influenced by environment/config sources and is fetched over plain HTTP with no validation that it is truly local or trusted. If an attacker can set WXAPI_BASE_URL/WXAPI_HOST/WXAPI_PORT or poison the status/config files, the tool may probe an attacker-controlled host and later send WeChat data and bearer tokens to it.
def check_service_alive(base_url):
"""Check if service is responding."""
try:
resp = requests.get(f"{base_url}/", timeout=3)
return resp.status_code == 200
except Exception:
return False
GET requests are sent to a base URL derived from environment variables and local config/status files, and they include a bearer token in the Authorization header. In this skill context, that means a poisoned configuration can redirect requests for status or metadata to an attacker-controlled server, leaking authentication material and enabling SSRF-like outbound access.
url = f"{BASE_URL}{endpoint}"
try:
if method == "GET":
resp = requests.get(url, headers=get_headers(), timeout=10)
else:
resp = requests.post(url, headers=get_headers(), json=data or {}, timeout=10)
return resp.json()
POST requests transmit contact identifiers and message content to whatever BASE_URL is resolved from environment/config/state files. In this WeChat automation skill, misdirecting these requests can exfiltrate private chats, friend/group data, and the bearer token to an attacker-controlled service.
if method == "GET":
resp = requests.get(url, headers=get_headers(), timeout=10)
else:
resp = requests.post(url, headers=get_headers(), json=data or {}, timeout=10)
return resp.json()
except requests.exceptions.ConnectionError:
print("Error: Cannot connect to service. Start with: python run.py")
The documented purpose understates important behaviors: the skill can auto-start and manage a backend service, read local status/config files for autodiscovery, and claims support for accepting friend requests without showing that functionality. This mismatch can mislead users and agents about the true authority and data access of the skill, increasing the chance of unintended process execution, local data exposure, or over-trust.
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
print(f"Starting service from {service_dir}...")
try:
# Set environment for proper encoding
env = os.environ.copy()
env['PYTHONIOENCODING'] = 'utf-8'
# Start service in background
The skill declares broad capabilities in practice—environment access, filesystem reads, network access, and shell/process control—without any explicit tool-scope restrictions. That makes the skill more dangerous than its metadata suggests, because an agent may invoke powerful behaviors such as reading local config, auto-starting services, or sending messages without a constrained permission model.
This skill can read chat history and monitor incoming WeChat messages, but the description does not prominently warn that it handles sensitive private communications. In context, that is security-relevant because users may authorize the skill for convenience without understanding it can access message content and ongoing conversation data.
The skill may automatically start a local backend service and perform automated message-sending actions, but that automation risk is not clearly disclosed as a high-visibility warning. In this context, hidden process launch plus automated messaging can cause unintended execution and actions on the user's WeChat account without informed approval.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
headers = {"Authorization": "Bearer token", "Content-Type": "application/json"} body = {"who": "好友名", "msg": "消息内容"} resp = requests.post("http://localhost:8000/v1/wechat/send", headers=headers, json=body) print(resp.json())
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
headers = {"Authorization": "Bearer token", "Content-Type": "application/json"} body = {"who": "好友名", "msg": "消息内容"} resp = requests.post("http://localhost:8000/v1/wechat/send", headers=headers, json=body) print(resp.json())
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.
headers = {"Authorization": "Bearer token", "Content-Type": "application/json"} body = {"who": "好友名", "msg": "消息内容"} resp = requests.post("http://localhost:8000/v1/wechat/send", headers=headers, json=body) print(resp.json())
The manifest describes WeChat automation functions such as sending messages, reading chats, listening for messages, and contact management via a RESTful API. In addition to calling that API, this code locates a separate service directory and launches run.py as a background subprocess, which is a host-level process management capability not stated in the skill purpose.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# Start service in background
if sys.platform == "win32":
subprocess.Popen(
[sys.executable, "run.py"],
cwd=str(service_dir),
env=env,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
creationflags=subprocess.CREATE_NO_WINDOW | subprocess.DETACHED_PROCESS
)
else:
subprocess.Popen(
[sys.executable, "run.py"],
cwd=str(service_dir),
env=env,
If no service is found, the tool may automatically start an external background process without explicit confirmation. In a security-sensitive environment, silent process creation increases attack surface and can cause unreviewed code in a discovered service directory to run under the user's account.
The generic API helper sends request data, including chat recipients and message bodies from commands like send and send-chat, over HTTP to the configured service. The file lacks any clear disclosure that user-provided message content and contact metadata will be transmitted to a local or configured API endpoint.
This code fetches chat history from the WeChat API and prints message contents and senders directly to stdout. Although the function has a docstring, there is no explicit user disclosure that running it will expose potentially sensitive private message content in terminal output or logs.
No suspicious patterns detected.