T09 · Insecure Skill Coding Practices
- Location
scripts/publish.sh:82- Finding
Arbitrary Shell Execution Through Shared Environment File Sourcing
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its WeChat publishing purpose, but it includes unsafe shell execution paths and overbroad credential/network handling that require review before installation.
Review or patch this skill before installing. At minimum, remove eval from scripts/publish.sh, stop sourcing ~/.openclaw/.env as shell code, read only the required WeChat variables, store AppSecret with restrictive permissions or a secret store, pin the install source, require confirmation before publishing, and restrict remote image/CSS fetching. Avoid using it with sensitive drafts until those issues are addressed.
scripts/publish.sh:82Arbitrary Shell Execution Through Shared Environment File Sourcing
scripts/publish.sh:149Command Injection Through Dynamically Constructed Eval Command
src/wechat_publish_pro/config.py:19Excessive Collection of Secrets From the Shared OpenClaw Environment
src/wechat_publish_pro/config.py:100Plaintext AppSecret Storage Without Enforced Restrictive Permissions
src/wechat_publish_pro/platforms/wechat.py:74Server-Side Request Forgery Through Unrestricted Remote Image Fetching
src/wechat_publish_pro/converter/wechat_style.py:559Unsanitized Markdown Content and URL Schemes in Generated HTML
\\1', text)
```
The title is also interpolated directly at `src/wechat_publish_pro/converter/wechat_style.py:76-79`:
```python
# 添加标题
if title:
html = f'SKILL.md:23Installation From a Mutable Unpinned Git Repository
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
把 Markdown 文章转换为微信公众号格式并发布到草稿箱。
pip install git+https://github.com/yuesf/wechat-publish-pro.git
# 初始化
wechat-publish-pro config init
# 微信公众号的凭证
wechat-publish-pro config set wechat.app_id <你的AppID>
wechat-publish-pro config set wechat.app_secret <你的AppSecret>
把运行机器的 IP 添加到微信公众号后台白名单
The installation instruction pulls code directly from a GitHub repository via 'pip install git+https://...', which bypasses the safety and reproducibility benefits of pinned package releases. In an agent context, remote bootstrap from a mutable repository can lead to execution of unreviewed or changed code, creating a software supply-chain risk.
封面生成*: 自动处理封面图,支持本地/网络图片
当用户需要以下操作时自动触发:
# 从 GitHub 安装
pip install git+https://github.com/yuesf/wechat-publish-pro.git
export WECHAT_APP_ID=your_wechat_app_id
export WECHAT_APP_SECRET=your_wechat_app_secret
# 初始化配置
wechat-publish-pro config init
# 设置配置
wechat-publish-pro config set wechat.app_id <AppID>
wechat-publish-pro config set wechat.app_secr
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_openclaw_env() -> dict[str, str]:
"""从 ~/.openclaw/.env 加载环境变量"""
env_file = Path.home() / ".openclaw" / ".env"
env_vars = {}
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_openclaw_env() -> dict[str, str]:
"""从 ~/.openclaw/.env 加载环境变量"""
env_file = Path.home() / ".openclaw" / ".env"
env_vars = {}
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_openclaw_env() -> dict[str, str]:
"""从 ~/.openclaw/.env 加载环境变量"""
env_file = Path.home() / ".openclaw" / ".env"
env_vars = {}
if env_file.exists():
with open(env_file, encoding="utf-8") as f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_openclaw_env() -> dict[str, str]:
"""从 ~/.openclaw/.env 加载环境变量"""
env_file = Path.home() / ".openclaw" / ".env"
env_vars = {}
if env_file.exists():
with open(env_file, encoding="utf-8") as f:
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
if properties:
rules.append(CSSRule(selector=selector, properties=properties))
return rules
def css_to_inline_style(rules: list[CSSRule]) -> Dict[str, str]:
The README states that the tool converts Markdown and publishes to the WeChat public account draft box, but it does not clearly warn users that article content and associated metadata will be transmitted to WeChat. This can mislead users about data handling and create privacy or confidentiality exposure when sensitive drafts are published through the skill.
The invocation examples are generic natural-language phrases like '把这篇文章发到公众号' and '帮我发布到微信,测试一下', which can overlap with ordinary conversation and cause accidental skill activation. In an agent setting, that raises the chance of unintended publication actions or content transfer to an external platform without sufficiently explicit user confirmation.
The skill advertises 'one-click publish' to the WeChat draft box but does not prominently warn that article content, cover images, and possibly remotely sourced media will be uploaded to WeChat. This omission can mislead users about the data-transfer consequences of using the skill, especially in an agent environment where actions may be automated.
The trigger phrases are broad enough to match ordinary user requests such as 'publish this article to WeChat' or 'use a blue theme,' which can cause the skill to activate automatically without a distinct confirmation boundary. Because this skill performs an external publishing action, overbroad activation increases the risk of unintended content upload to a third-party platform using stored credentials.
The comment states '完全独立实现,不依赖任何外部 CLI 工具' ('fully independently implemented, does not rely on any external CLI tools'). However, the script explicitly checks for and uses external tooling such as python3 and invokes the wechat-publish-pro publisher command, so the documentation contradicts the actual behavior.
The script sources $HOME/.openclaw/.env and exports its contents, which is a sensitive credential access path because the help text identifies WECHAT_APP_ID and WECHAT_APP_SECRET as required environment variables. Although there is a log indicating the file is loaded, there is no explicit warning or disclosure that secrets will be read from a local credential file.
The save() method serializes app_id and app_secret directly into ~/.wechat-publish-pro/config.yaml in plaintext. Storing long-lived API credentials unencrypted on disk increases the risk of credential disclosure through local compromise, backups, sync tools, or overly permissive file permissions.
The hardcoded external API endpoint indicates that document content may be sent off-host to a third-party service during conversion. In this skill context, Markdown may contain proprietary or sensitive text, so external transmission creates confidentiality and compliance risk even if the code is not overtly malicious.
"""Markdown 转换器"""
# mdnice API 端点
MDNICE_API = "https://api.mdnice.com/api/v1/markdown"
def __init__(self, options: Optional[ConvertOptions] = None) -> None:
self.options = options or ConvertOptions()
When API mode is enabled, the full Markdown content is transmitted to a third-party or user-specified endpoint without any visible disclosure or consent mechanism at the call site. This can expose sensitive document contents, embedded secrets, private links, or unpublished material to external services, and the risk is higher because the endpoint can be overridden dynamically.
The function accepts attacker-controlled URLs and fetches remote CSS without restriction, allowing untrusted network access from the host running the skill. This can enable SSRF-style behavior against internal services, unexpected data exfiltration via request metadata, or privacy issues from contacting external servers during theme loading.
The docstring states that the code has already been HTML-escaped and is therefore safe to wrap with span tags, but _render_code_block only escapes &, <, and > before calling _highlight_code_escaped. Because quotes are not escaped, the regex substitutions in L274-L275 that assume " and ' never match, and more importantly the safety claim in the docstring is inaccurate relative to the actual escaping performed.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
class WeChatPlatform(Platform):
"""微信公众号平台"""
BASE_URL = "https://api.weixin.qq.com/cgi-bin"
def __init__(self, app_id: str, app_secret: str) -> None:
self.app_id = app_id
The code sends app_id and app_secret to the WeChat API to obtain an access token, but there is no user-facing notice, logging, or warning comment indicating that configured credentials are transmitted over the network. This is a safety-relevant external operation involving sensitive authentication material.
The adapter accepts arbitrary http/https URLs for image uploads and fetches them server-side without any allowlist, size limits, or destination validation. If an attacker can control image_path, this creates an SSRF primitive that can be used to probe internal services, access cloud metadata endpoints, or force the server to make unexpected outbound requests.
The function accepts either a remote URL or a local path, then silently downloads remote content or reads a local file for upload to WeChat. There is no confirmation prompt, print/log message, or warning comment/docstring disclosing that the skill may fetch external data and transmit file contents to a third-party service.
Natural-language policy checks apply to all file types. The file presents the skill content entirely in Chinese and does not offer an explicit language choice or explain that the skill is intended only for Chinese-speaking users, which may amount to forcing a specific language without user opt-in.
This is a markdown file, so missing-warning checks apply to the natural-language description. The feature list advertises uploading images and publishing to a WeChat draft box, which can affect user data or external accounts, but the document provides no warning or disclosure about these actions.
The package description is written entirely in Chinese and provides no indication that other languages are supported or that the locale restriction is intentional. Under the stated policy, language constraints should either be user-selectable or clearly documented as justified.
No suspicious patterns detected.