T09 · Insecure Skill Coding Practices
- Location
scripts/push_draft.py:49- Finding
WeChat credentials and access tokens exposed through command-line arguments
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This WeChat publishing skill is coherent, but it enables irreversible scheduled publishing and exposes account credentials in ways users should review before installing.
Review carefully before installing. Use this only for a WeChat account where scheduled publication is intended, store credentials with restricted permissions or a secret manager, avoid unattended publish automation unless your workflow has a separate approval check, and treat delete/publish operations as irreversible account actions.
scripts/push_draft.py:49WeChat credentials and access tokens exposed through command-line arguments
scripts/push_draft.py:111Predictable shared temporary file permits symlink overwrite, data leakage, and cross-run corruption
The documented behavior does not fully match the apparent capability set: it advertises auto-publish setup that is not actually implemented while omitting other draft-management and upload capabilities. This mismatch is dangerous because users and orchestrators may authorize the skill based on an incomplete understanding, enabling broader or more destructive actions such as deleting drafts or managing assets without clear consent.
The template instructs the automation to read WECHAT_APPID and WECHAT_SECRET from a .env file. Accessing plaintext credentials from a general-purpose automation increases exposure risk through accidental logging, prompt leakage, path substitution mistakes, or reuse by other commands within the workflow.
- If the file exists: read the `media_id` field.
### Step 3: Load credentials
Read the .env file at {ENV_FILE_PATH} for WECHAT_APPID and WECHAT_SECRET.
### Step 4: Publish the draft
1. Get access_token:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Requirements:
- curl
- A .env file with WECHAT_APPID and WECHAT_SECRET
- The account must be a verified enterprise/organization (freepublish permission)
"""
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Load credentials
creds = load_credentials(args.env)
# Get access token
token = get_access_token(creds["WECHAT_APPID"], creds["WECHAT_SECRET"])
# Attempt publish with retries for transient system errors
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Load credentials
creds = load_credentials(args.env)
# Get access token
token = get_access_token(creds["WECHAT_APPID"], creds["WECHAT_SECRET"])
# Attempt publish with retries for transient system errors
The skill describes operations that require shell, network, and file access, but it does not declare any explicit tool scope or allowed-tools boundaries. This creates an overprivilege and transparency problem: an agent or reviewer cannot easily constrain execution to the minimum required capabilities, increasing the chance of unintended file access, credential exposure, or arbitrary command execution through the referenced scripts.
The skill promotes daily auto-publish automation but does not place a prominent warning at the automation setup point that publication sends content to all subscribers without a mandatory human approval step. In this context, silent automation of an irreversible mass-publishing action materially raises the risk of accidental or malicious publication, reputational damage, and distribution of unreviewed content.
The guide states that publishing is irreversible and immediately sends the article to all subscribers, but it lacks operational safeguards such as confirmation, scheduling validation, or pre-publish review steps. In an automation skill, this can cause unauthorized or accidental mass publication with reputational and business impact.
POST https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=TOKEN
Body: {"media_id": "DRAFT_ID"}
{"errcode": 0, "errmsg": "ok", "publish_id": "..."}The guide includes a destructive delete-draft API call without warning about permanence, confirmation, or safe-guarding. In an automation/publishing skill, omission of such guardrails can lead to accidental content loss or unintended destructive actions, especially if an agent operationalizes the reference directly.
The delete-draft endpoint enables destructive remote action against user content, and the guide presents it without safety controls or warnings. In the context of an automated publishing toolkit, this increases the risk of accidental or unauthorized deletion of drafts if the reference is implemented naively.
POST https://api.weixin.qq.com/cgi-bin/draft/delete?access_token=TOKEN
Body: {"media_id": "DRAFT_ID"}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
Do NOT use:
# WRONG — garbles Chinese
requests.post(url, json=draft_data)
DO use:
The manifest describes a WeChat draft/publish toolkit, where making HTTP requests is expected, but this documentation specifically instructs use of Python's subprocess module to invoke an external shell utility. Spawning external programs is a qualitatively broader capability than direct API usage and is not justified by the declared purpose when the same task is simply sending an HTTP POST.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
with open(json_path, "w", encoding="utf-8") as f:
json.dump(draft_data, f, ensure_ascii=False, separators=(",", ":"))
# 2. Upload with curl --data-binary (preserves UTF-8 bytes)
subprocess.run([
"curl", "-s", "-X", "POST", url,
"-H", "Content-Type: application/json",
The manifest describes a WeChat draft push and publishing toolkit, but this template explicitly directs the automation to execute a shell command (date +%Y-%m-%d) as part of its operation. While publishing itself is in-scope, arbitrary command execution is a broader capability than what the stated purpose justifies.
The same instruction block also includes an outbound request to api.ipify.org, a third-party service unrelated to WeChat publishing. Even though the finding is grouped under the WeChat URL location, the actual risky behavior is the unnecessary external disclosure of the automation host's public IP to another service.
### Step 4: Publish the draft
1. Get access_token:
curl -s "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET"
2. If errcode=40164 (IP whitelist): get server IP with `curl -s https://api.ipify.org` and notify the user to add it to the whitelist.
3. Call publish API:
curl -X POST "https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=TOKEN" \
The same instruction block also includes an outbound request to api.ipify.org, a third-party service unrelated to WeChat publishing. Even though the finding is grouped under the WeChat URL location, the actual risky behavior is the unnecessary external disclosure of the automation host's public IP to another service.
### Step 4: Publish the draft
1. Get access_token:
curl -s "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET"
2. If errcode=40164 (IP whitelist): get server IP with `curl -s https://api.ipify.org` and notify the user to add it to the whitelist.
3. Call publish API:
curl -X POST "https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=TOKEN" \
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_access_token(appid, secret):
"""Get WeChat access_token. Handle IP whitelist error (40164)."""
result = subprocess.run(
[
"curl", "-s",
f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}",
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_access_token(appid, secret):
"""Get WeChat access_token. Handle IP whitelist error (40164)."""
result = subprocess.run(
[
"curl", "-s",
f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
result = subprocess.run(
[
"curl", "-s",
f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}",
],
capture_output=True,
text=True,
Detected: suspicious.exposed_secret_literal