T09 · Insecure Skill Coding Practices
- Location
SKILL.md:34- Finding
WeChat AppSecret Persisted in a Shell Startup File
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to be a real WeChat draft-management tool, but it handles account credentials and can publish or delete content with weak local secret-storage and confirmation guidance.
Install only if you are comfortable giving the tool WeChat Official Account API authority to upload media, read drafts, create drafts, submit publications, and delete drafts. Avoid putting the AppSecret in ~/.zshrc; use a dedicated secret manager or tightly permissioned local environment file, and harden or disable the token cache if possible. Treat publish and delete commands as irreversible account actions and confirm the target media_id before use.
SKILL.md:34WeChat AppSecret Persisted in a Shell Startup File
scripts/channel.py:64WeChat Bearer Access Token Cached Without Explicit Permission Hardening
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode('utf-8'))
if 'access_token' in data:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode('utf-8'))
if 'access_token' in data:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode('utf-8'))
if 'access_token' in data:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode('utf-8'))
if 'access_token' in data:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as response:
data = json.loads(response.read().decode('utf-8'))
if 'access_token' in data:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
'Content-Length': len(data)
})
with urllib.request.urlopen(req, timeout=60) as response:
result = json.loads(response.read().decode('utf-8'))
if 'url' in result:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
'Content-Length': len(data)
})
with urllib.request.urlopen(req, timeout=60) as response:
result = json.loads(response.read().decode('utf-8'))
if 'url' in result:
The documented purpose understates actual behavior by omitting automatic cover generation, temporary file creation, use of the external sips utility, and comment-control settings. Description/behavior mismatches are dangerous because users and policy systems may authorize the skill under false assumptions, while the hidden behaviors expand local execution and content-modification surface.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
os.makedirs(CONFIG_DIR, exist_ok=True)
def get_access_token() -> Optional[str]:
"""获取 Access Token"""
appid = os.getenv('WECHAT_APPID')
appsecret = os.getenv('WECHAT_APPSECRET')
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
os.makedirs(CONFIG_DIR, exist_ok=True)
def get_access_token() -> Optional[str]:
"""获取 Access Token"""
appid = os.getenv('WECHAT_APPID')
appsecret = os.getenv('WECHAT_APPSECRET')
The script caches the retrieved access token under ~/.config/channel/access_token.json without setting restrictive file permissions or validating the directory/file mode. On multi-user systems or misconfigured environments, another local user or process may read the token and act on the associated WeChat account until expiry.
return token
else:
print(f"❌ 获取 Access Token 失败: {data.get('errmsg', 'Unknown error')}")
return None
except Exception as e:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
parser.print_help()
return
# 获取 access token
access_token = get_access_token()
if not access_token:
return
The skill declares access to secrets and describes file, shell, and network-backed behavior, but it does not define any explicit tool scope or permission boundaries. In an agent setting, this can lead to over-broad execution authority, making accidental secret exposure, unintended file access, or unsafe command execution more likely if the implementation is invoked with excessive privileges.
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.
---
name: channel
description: WeChat Official Account Draft Box management tool. Create and manage graphic draft articles via WeChat API, supporting text and images. Automatically extracts the first paragraph as summary. Supports draft creation, listing, publishing, and deletion.
env:
- WECHAT_APPID
- WECHAT_APPSECRET
The skill exposes a destructive delete operation without warning that it permanently removes drafts or recommending confirmation before use. In automation contexts, this increases the chance of irreversible content loss from operator error, prompt injection, or misuse by downstream workflows.
The deletion endpoint is documented as a normal operation without any warning that it permanently removes draft content. In a content-management skill, undocumented destructive actions increase the risk of accidental data loss, especially if an agent or user invokes the endpoint without confirmation safeguards.
The document instructs users to upload local image files to WeChat using multipart/form-data but does not clearly warn that the file contents are transmitted to a third-party external service. In a skill that manages WeChat drafts and media, this omission can lead to inadvertent disclosure of sensitive local images or embedded metadata.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
}] }
response = requests.post(url, json=data)
return response.json()
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from typing import List, Dict, Optional
# 微信 API 配置
WECHAT_API_BASE = "https://api.weixin.qq.com/cgi-bin"
# 存储文件
CONFIG_DIR = os.path.expanduser("~/.config/channel")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from typing import List, Dict, Optional
# 微信 API 配置
WECHAT_API_BASE = "https://api.weixin.qq.com/cgi-bin"
# 存储文件
CONFIG_DIR = os.path.expanduser("~/.config/channel")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from typing import List, Dict, Optional
# 微信 API 配置
WECHAT_API_BASE = "https://api.weixin.qq.com/cgi-bin"
# 存储文件
CONFIG_DIR = os.path.expanduser("~/.config/channel")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from typing import List, Dict, Optional
# 微信 API 配置
WECHAT_API_BASE = "https://api.weixin.qq.com/cgi-bin"
# 存储文件
CONFIG_DIR = os.path.expanduser("~/.config/channel")
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from typing import List, Dict, Optional
# 微信 API 配置
WECHAT_API_BASE = "https://api.weixin.qq.com/cgi-bin"
# 存储文件
CONFIG_DIR = os.path.expanduser("~/.config/channel")
No suspicious patterns detected.