T09 · Insecure Skill Coding Practices
- Location
scripts/get_token.py:54- Finding
Plaintext Access Token Cache Without Restrictive File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill does what it claims, but it handles WeChat account credentials and stores access tokens locally without protective file permissions.
Install only if you are comfortable giving the skill WeChat Official Account API credentials and having article text, metadata, and selected images uploaded to WeChat. Prefer environment variables or a protected secrets manager, keep config.json and .token_cache.json out of source control, restrict token-cache permissions, and review generated drafts before publishing.
scripts/get_token.py:54Plaintext Access Token Cache Without Restrictive File Permissions
scripts/md_to_html.py:92Unsanitized Markdown and URL Values Can Inject Unsafe Draft HTML
\\1', text)
# Links
text = re.sub(r'\[(.+?)\]\((.+?)\)', r'\1', text)
return text
```
Image source and alternative-text values are also interpolated without escaping:
```python
# Image
m = re.match(r'^!\[([^\]]*)\]\(([^)]+)\)', stripped)
if m:
alt, src = m.group(1), m.group(2)
html_lines.append(f'Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}).encode()
req = urllib.request.Request(TOKEN_URL, data=payload,
headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read())
if "errcode" in data and data["errcode"] != 0:
print(f"ERROR: WeChat API error: {data}", file=sys.stderr)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Get access token
python3 scripts/get_token.py
# Upload cover image → get thumb_media_id
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Get access token
python3 scripts/get_token.py
# Upload cover image → get thumb_media_id
The declared description presents a full WeChat draft publishing skill: managing API tokens, uploading images to WeChat CDN, converting Markdown, and creating a draft in the公众号 draft box. The supplied code chunk does only one subset task: Markdown-to-HTML conversion with optional substitution of image URLs from a JSON mapping file. It performs no network access, no authentication, no token handling, no CDN upload, and no draft creation via API. Because the actual behavior is materially narrower and lacks the core publishing/API capabilities described, this is a clear description-behavior mismatch.
The code’s core behavior is narrower than the declared description. It does perform token retrieval and draft creation for a WeChat Official Account, which aligns partially with the stated purpose. However, this script only accepts HTML input (--html or --html-content) and requires a preexisting thumb_media_id; it does not convert Markdown to HTML, discover or upload local images, or generate/upload image assets to WeChat CDN. Therefore, the declared description overstates the implemented capabilities of this code chunk.
The skill states that access tokens are cached in a local .token_cache.json file, which creates a credential-handling risk if file permissions are weak, the workspace is shared, or logs/backups expose the cache. While token caching is operationally common, storing bearer-style tokens on disk without documented protections can enable account misuse if the file is read by another process or user.
## Notes
- Access tokens are cached in `.token_cache.json` (auto-refreshed when expired)
- WeChat only accepts images hosted on WeChat CDN — always upload images first
- The draft is saved but NOT published; user must publish manually from MP backend or via `/cgi-bin/freepublish/submit`
- Subscription accounts (订阅号) may have limited publishing frequency (once per day/week)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
#!/usr/bin/env python3
"""
Get (and cache) WeChat Official Account Access Token.
Usage: python3 get_token.py
Output: prints the access_token string to stdout
Config: reads APPID and APPSECRET from ../config.json or env vars WX_APPID / WX_APPSECRET
File contents flow to a network sink. This may indicate data exfiltration of sensitive files.
req = urllib.request.Request(url, data=body,
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
method="POST")
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read())
if "errcode" in data and data["errcode"] != 0:
File contents flow to a network sink. This may indicate data exfiltration of sensitive files.
req = urllib.request.Request(url, data=body,
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
method="POST")
with urllib.request.urlopen(req) as resp:
data = json.loads(resp.read())
if "errcode" in data and data["errcode"] != 0:
The README promotes an automated workflow that uploads article content and images to WeChat services, but it does not clearly warn users that local files and article data will be transmitted to a third-party platform. In a publishing skill, this omission can cause accidental disclosure of private, unpublished, or sensitive content because users may assume processing is local or not appreciate the full data flow.
The skill directs execution of Python scripts, reads local files, writes temporary files, uses environment variables for credentials, and performs networked uploads, but it declares no explicit tool scope or permission boundaries. This increases the chance an agent will invoke sensitive capabilities without clear user consent or policy enforcement, especially because the workflow handles local content and secrets.
The skill instructs the user to upload article text and local images to WeChat, but it does not prominently disclose that local files and potentially sensitive content will be transmitted to a third-party service. In an agent setting, missing disclosure can cause unintended exfiltration of proprietary drafts, personal data, or unpublished media.
The reference shows storing long-lived WeChat app credentials in a local config file and environment variables without any warning about secret handling, file permissions, rotation, or avoiding commits to source control. In a skill that manages authentication tokens for a production publishing API, this omission can lead users to expose app secrets, enabling unauthorized API access and account abuse.
The file describes conversion specifically to 'WeChat-compatible HTML' and embeds Chinese-platform-specific assumptions such as WeChat CDN URLs and Chinese font preferences. Because the script is framed as a general Markdown-to-HTML converter rather than clearly as a region-specific tool with opt-in locale selection, this can violate language/locale policy expectations.
The manifest describes a WeChat publishing skill that handles token management, image upload, content conversion, and draft creation. In this file, token retrieval is implemented by launching another Python process via subprocess, which adds process-execution capability beyond the obvious needs of publishing a draft.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import json, sys, urllib.request
from pathlib import Path
UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material"
def get_token():
import subprocess
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_token():
import subprocess
result = subprocess.run(
["python3", Path(__file__).parent / "get_token.py"],
capture_output=True, text=True
)
No suspicious patterns detected.