T09 · Insecure Skill Coding Practices
- Location
index.js:180- Finding
WeChat Access Token Disclosed in Plaintext Console Output
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to publish WeChat articles as advertised, but it exposes WeChat credentials/tokens and handles publishable HTML unsafely enough that users should review it before installing.
Install only after replacing any example secrets with placeholders, rotating any AppSecret that may have been published, removing or masking the token command output, adding explicit .env protection such as .gitignore guidance, sanitizing generated HTML before save or publish, and updating the flagged npm dependencies.
index.js:180WeChat Access Token Disclosed in Plaintext Console Output
article.md:117Credential-Like WeChat AppID and AppSecret Embedded in Published Project Content
index.js:7Untrusted Markdown and Raw HTML Are Published Without Sanitization
$1')
// 代码块
.replace(/```(\w*)\n([\s\S]*?)```/gim, '$2👉 $1
') // 分割线 .replace(/^---$/gim, '')
// 普通换行
.replace(/\n/gim, '
');
// 包裹在最外层
html = '
' + html + '
'; return html; } ``` The resulting HTML can be saved locally: ```js const htmlContent = markdownToHtml(content); // 调试:输出 HTML 长度 console.log(`📊 HTML 内容长度:${htmlContent.length} 字符`); // 保存 HTML 到本地(用于调试或手动复制) if (saveHtml) { const fs = require('fs'); const htmlPath = path.join(__dirname, 'output.html'); fs.writeFileSync(htmlPath, htmlContent, 'utf-8'); console.log(`💾 HTML 已保存到:${htmlPath}`); } ``` It is also submitted as article content: ```js const response = await axios.post(`${this.draftUrl}?access_token=${token}`, draftData); ``` ### Technical Analysis The converter does not HTML-escape source content before inserting it into HTML. Existing tags, event-handler attributes, active elements, and unsafe URL schemes can therefore survive conversion. Content can originate from either the `--content` argument or a file selected using `--f ...[truncated 2105 chars]Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// 加载环境变量
const path = require('path');
const dotenv = require('dotenv');
const envPath = path.resolve(__dirname, '.env');
dotenv.config({ path: envPath });
// 简单的 Markdown 转 HTML(针对微信公众号优化 - 简洁版)
This line emits the retrieved access token in plaintext, turning credential access into credential exposure. In shared environments, logs or console capture can leak the token to unauthorized parties who can then invoke WeChat APIs on behalf of the account.
try {
if (command === 'token') {
const token = await mp.getAccessToken();
console.log(`Access Token: ${token}`);
} else if (command === 'publish') {
const title = args.find((a, i) => args[i - 1] === '--title');
const content = args.find((a, i) => args[i - 1] === '--content');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
<p><h1>手把手教你配置 OpenClaw 微信公众号发布技能(避坑指南)</h1></p><p>> 文 / 卡哥 <br/>> 这是「卡哥 AI 实操」的第 1 篇原创</p><p><hr/></p><p><h2>为什么写这篇文章</h2></p><p>昨天有个粉丝问我:"卡哥,能不能让 AI 自动发公众号文章?"</p><p>我说能,但需要配置一下。</p><p>结果他折腾了一下午,卡在 IP 白名单那一步动不了。</p><p>所以我决定把<b>完整的配置过程</b>写下来,包括我踩过的所有坑。</p><p>你照着做,<b>30 分钟搞定</b>。</p><p><hr/></p><p><h2>开始前需要准备的东西</h2></p><p><p>👉 <b>已注册的微信公众号</b>(订阅号或服务号都行)</p><br/><p>👉 <b>一台能联网的电脑</b>(Windows / Mac 都可以)</p><br/><p>👉 <b>Node.js 环境</b>(如果没装,后面会教)</p><br/><p>👉 <b>你的 AppID 和 AppSecret</b>(公众号后台获取)</p></p><p><hr/></p><p><h2>第一步:获取 AppID 和 AppSecret</h2></p><p>登录微信公众平台:https://mp.weixin.qq.com</p><p>扫码登录后,点击左侧菜单的 <b>开发 → 基本配置</b></p><p>在这个页面你能看到 AppID(类似 wx1ec4bd8707cd30c4)和 AppSecret。</p><p><b>注意:</b><br/>- AppSecret 需要点击"重置"才能看到(重置后旧的会失效)<br/>- <b>把这两个值复制到记事本</b>,后面要用</p><p><b>⚠️ 易错点:</b><br/>- 不要直接截图保存 AppSecret(不安全)<br/>- 复制时注意不要多复制空格</p><p><hr/></p><p><h2>第二步:配置 IP 白名单(最容易卡住的地方)</h2></p><p>还在"基本配置"页面,往下翻,找到 <b>IP 白名单</b></p><p>点击"修改",添加你的服务器 IP。</p><p><b>如果你不知道自己的 IP 是什么:</b></p><p>打开浏览器,访问:https://ip138.com</p><p>页面上显示的第一个 IP 就是你的公网 IP。</p><p><b>⚠️ 易错点(我在这栽了两次):</b><br/>- 家里宽带的 IP 会变(动态 IP),每次变了都要重新配置<br/>- 最好用固定 IP 的服务器<br/>- 配置后需要等 1-2 分钟才生效<br/>- <b>不要配成内网 IP(192.168.x.x 或 10.x.x.x)</b></p><p><hr/></p><p><h2>第三步:安装 Node.js(如果已安装可跳过)</h2></p><p>访问 Node.js 官网:https://nodejs.org</p><p>下载 LTS 版本(长期支持版)</p><p>下载后一路下一步安装。</p><p><b>验证安装:</b></p><p>打开命令行(Windows 按 Win+R,输入 <code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;">cmd</code>),输入:</p><p><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`bash<br/>node -v<br/>npm -v<br/><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`</p><p>如果显示版本号,说明安装成功。</p><p><b>⚠️ 易错点:</b><br/>- 安装后需要<b>重启命令行</b>才能生效<br/>- 不要安装最新版(可能有兼容性问题),选 LTS</p><p><hr/></p><p><h2>第四步:创建技能目录</h2></p><p>打开命令行,输入:</p><p><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`bash<br/>mkdir C:\Users\你的用户名\.openclaw\skills\wechat-mp-publisher-1.0.0<br/>cd C:\Users\你的用
...[truncated 26 chars]
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
<p><h1>手把手教你配置 OpenClaw 微信公众号发布技能(避坑指南)</h1></p><p>> 文 / 卡哥 <br/>> 这是「卡哥 AI 实操」的第 1 篇原创</p><p><hr/></p><p><h2>为什么写这篇文章</h2></p><p>昨天有个粉丝问我:"卡哥,能不能让 AI 自动发公众号文章?"</p><p>我说能,但需要配置一下。</p><p>结果他折腾了一下午,卡在 IP 白名单那一步动不了。</p><p>所以我决定把<b>完整的配置过程</b>写下来,包括我踩过的所有坑。</p><p>你照着做,<b>30 分钟搞定</b>。</p><p><hr/></p><p><h2>开始前需要准备的东西</h2></p><p><p>👉 <b>已注册的微信公众号</b>(订阅号或服务号都行)</p><br/><p>👉 <b>一台能联网的电脑</b>(Windows / Mac 都可以)</p><br/><p>👉 <b>Node.js 环境</b>(如果没装,后面会教)</p><br/><p>👉 <b>你的 AppID 和 AppSecret</b>(公众号后台获取)</p></p><p><hr/></p><p><h2>第一步:获取 AppID 和 AppSecret</h2></p><p>登录微信公众平台:https://mp.weixin.qq.com</p><p>扫码登录后,点击左侧菜单的 <b>开发 → 基本配置</b></p><p>在这个页面你能看到 AppID(类似 wx1ec4bd8707cd30c4)和 AppSecret。</p><p><b>注意:</b><br/>- AppSecret 需要点击"重置"才能看到(重置后旧的会失效)<br/>- <b>把这两个值复制到记事本</b>,后面要用</p><p><b>⚠️ 易错点:</b><br/>- 不要直接截图保存 AppSecret(不安全)<br/>- 复制时注意不要多复制空格</p><p><hr/></p><p><h2>第二步:配置 IP 白名单(最容易卡住的地方)</h2></p><p>还在"基本配置"页面,往下翻,找到 <b>IP 白名单</b></p><p>点击"修改",添加你的服务器 IP。</p><p><b>如果你不知道自己的 IP 是什么:</b></p><p>打开浏览器,访问:https://ip138.com</p><p>页面上显示的第一个 IP 就是你的公网 IP。</p><p><b>⚠️ 易错点(我在这栽了两次):</b><br/>- 家里宽带的 IP 会变(动态 IP),每次变了都要重新配置<br/>- 最好用固定 IP 的服务器<br/>- 配置后需要等 1-2 分钟才生效<br/>- <b>不要配成内网 IP(192.168.x.x 或 10.x.x.x)</b></p><p><hr/></p><p><h2>第三步:安装 Node.js(如果已安装可跳过)</h2></p><p>访问 Node.js 官网:https://nodejs.org</p><p>下载 LTS 版本(长期支持版)</p><p>下载后一路下一步安装。</p><p><b>验证安装:</b></p><p>打开命令行(Windows 按 Win+R,输入 <code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;">cmd</code>),输入:</p><p><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`bash<br/>node -v<br/>npm -v<br/><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`</p><p>如果显示版本号,说明安装成功。</p><p><b>⚠️ 易错点:</b><br/>- 安装后需要<b>重启命令行</b>才能生效<br/>- 不要安装最新版(可能有兼容性问题),选 LTS</p><p><hr/></p><p><h2>第四步:创建技能目录</h2></p><p>打开命令行,输入:</p><p><code style="background:#f5f5f5;padding:2px 6px;border-radius:3px;"></code>`bash<br/>mkdir C:\Users\你的用户名\.openclaw\skills\wechat-mp-publisher-1.0.0<br/>cd C:\Users\你的用
...[truncated 26 chars]
The article shows a plaintext AppSecret in a configuration snippet and instructs readers to place secrets into a .env file, which is a common leakage path in shared skill packages, backups, screenshots, and accidental commits. Because this skill is specifically about automating publication to a WeChat account, compromise of that secret could enable unauthorized token acquisition and content publishing under the victim account.
The lockfile pins axios to 1.13.6, and the provided advisories indicate multiple known issues including SSRF/proxy-bypass and prototype-pollution-related attack paths. In a publisher/integration skill that likely makes outbound HTTP requests and may handle secrets or tokens, a vulnerable HTTP client can enable request redirection, credential exposure, or abuse of network trust boundaries.
The lockfile includes form-data 4.0.5 with a reported CRLF injection issue in multipart field names/filenames. If this skill constructs multipart uploads from untrusted input, an attacker may be able to manipulate multipart boundaries or inject unexpected headers/content, which can lead to request smuggling-like effects against upstream APIs or data integrity issues.
The dependency declaration ^1.6.0 permits installation of axios 1.13.6, and the finding reports that resolved version as having multiple published advisories, including SSRF- and prototype-pollution-related issues. In a publishing skill that likely makes outbound HTTP requests to WeChat APIs, a vulnerable HTTP client is especially sensitive because request routing, headers, credentials, or response handling could be abused depending on how the library is used.
The reported installed version form-data 4.0.5 is flagged for CRLF injection in multipart field names/filenames. In a skill that likely uploads media or article assets to a remote API, malformed or attacker-influenced multipart metadata could enable request smuggling or header manipulation against downstream services.
The document includes a concrete-looking AppSecret value in example .env content immediately after warning users to protect the secret. Even if the value is illustrative, publishing secret-shaped credentials normalizes unsafe handling and can lead users to copy real credentials into insecure places or mistakenly treat the example as valid sensitive data.
The instructions tell users to store live credentials in a .env file but omit key safeguards such as excluding the file from version control and avoiding credential exposure in terminal history, logs, or screenshots. In a skill focused on automation and publishing, this omission materially increases the likelihood of credential leakage and unauthorized access to the WeChat account.
User-facing comments, status messages, defaults, and help text are written in Chinese throughout the file, including the default author name and CLI usage strings. The file does not offer a language choice or explain that the skill is intentionally limited to a Chinese-speaking or region-specific context.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
constructor() {
this.appId = process.env.WECHAT_APPID;
this.appSecret = process.env.WECHAT_APPSECRET;
this.tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token';
this.draftUrl = 'https://api.weixin.qq.com/cgi-bin/draft/add';
this.publishUrl = 'https://api.weixin.qq.com/cgi-bin/freepublish/batchpublish';
}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
constructor() {
this.appId = process.env.WECHAT_APPID;
this.appSecret = process.env.WECHAT_APPSECRET;
this.tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token';
this.draftUrl = 'https://api.weixin.qq.com/cgi-bin/draft/add';
this.publishUrl = 'https://api.weixin.qq.com/cgi-bin/freepublish/batchpublish';
}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
constructor() {
this.appId = process.env.WECHAT_APPID;
this.appSecret = process.env.WECHAT_APPSECRET;
this.tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token';
this.draftUrl = 'https://api.weixin.qq.com/cgi-bin/draft/add';
this.publishUrl = 'https://api.weixin.qq.com/cgi-bin/freepublish/batchpublish';
}
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
constructor() {
this.appId = process.env.WECHAT_APPID;
this.appSecret = process.env.WECHAT_APPSECRET;
this.tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token';
this.draftUrl = 'https://api.weixin.qq.com/cgi-bin/draft/add';
this.publishUrl = 'https://api.weixin.qq.com/cgi-bin/freepublish/batchpublish';
}
The CLI includes a dedicated token command that retrieves and prints the WeChat access token to stdout. Even if intended for debugging, exposing bearer tokens through terminal output, logs, shell history capture, or orchestration systems increases the chance of credential leakage and unauthorized API use.
Printing the access token directly to stdout exposes a live credential to anyone who can view terminal output, CI logs, shell recording tools, or redirected files. Because the token authorizes API actions against the associated WeChat account, disclosure can enable unauthorized publishing or content management until expiry.
The guide includes what appears to be a real-looking AppSecret directly in the example .env snippet, normalizing copying sensitive credentials into local plaintext configuration and potentially exposing a live secret if readers reuse or trust the sample value. In a skill-distribution context, embedded secrets can be harvested, accidentally propagated, or left unrotated, creating account compromise risk.
SQP-3 适用于所有文件类型。该技能说明以中文撰写且未给出其他语言选项或明确声明仅面向中文用户,可能与要求提供语言/locale 选择的组织政策相冲突。
The inline comment describes saving HTML locally 'for debugging or manual copy,' which implies a local-only aid, but createDraft continues on to submit the generated content to the WeChat draft API at L132 regardless of saveHtml. This is a mild intent/documentation contradiction because the save operation is not an alternative to remote submission.
A language or locale policy issue can arise when a skill forces a specific language without user opt-in. This file presents all user-facing instructions solely in Chinese and does not indicate that language selection is optional or that the content is region-specific by design.
follow-redirects is a transitive dependency of axios and the cited advisory indicates custom authentication headers may be leaked across cross-domain redirects. For a skill that likely interacts with external APIs using bearer tokens or app credentials, this can expose sensitive headers to attacker-controlled endpoints if redirects are followed.
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
"publish": "node index.js publish"
},
"dependencies": {
"axios": "^1.6.0",
"dotenv": "^17.3.1",
"form-data": "^4.0.5"
},
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.
},
"dependencies": {
"axios": "^1.6.0",
"dotenv": "^17.3.1",
"form-data": "^4.0.5"
},
"keywords": [
Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal