Back to skill

Security audit

wx-mp-push

Security checks for vulnerabilities and agentic risk

Overview

This WeChat publishing skill is mostly purpose-aligned, but it needs review because it can read local image paths, fetch remote URLs, cache tokens, and send credentials or draft data with insufficient safeguards.

Review before installing. Use only trusted article content and configs, keep `apiBaseUrl` pinned to the real WeChat API, avoid external image URLs unless you intend them to be fetched and uploaded, run from a narrow content directory, and protect or disable the token cache on shared machines.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/publish_article.py:459
Finding

Arbitrary Local File Upload Through Unrestricted Image Paths

Content
View full analysis
]*src=["\']([^"\']+)["\'][^>]*>' matches = re.findall(img_pattern, content, re.IGNORECASE) uploaded_images = {} processed_content = content image_counter = 1 for src in matches: is_url = src.startswith(('http://', 'https://')) temp_path = None try: if is_url: print(f" [{image_counter}] {src[:60]}... - External URL, downloading...") temp_path = await self._download_url_to_temp(src) image_path = temp_path display_name = src.split('/')[-1].split('?')[0] or f'url_image_{image_counter}' else: if os.path.isabs(src): image_path = src else: image_path = os.path.join(base_dir, src) display_name = os.path.basename(src) result = await self.upload_image(image_path, is_thumb=False) ``` ### Technical Analysis The script extracts image paths directly from article HTML and uses them as local filesystem paths. Absolute paths are explicitly accepted, while relative paths are joined to `base_dir` without canonicalization or containment validation. Consequently, traversal sequences such as `../` can escape the intended content directory. The later extension check only verifies the filename suffix and does not establish that the caller is authorized to read or upload the file. Any readable local file with an accepted image extension can therefore be transmitted to the configured API endpoint. ### Attack Path 1. An attacker supplies or influences Markdown or HTML article content. 2. The content includes an image such as: ```html ``` An absolute path can also be used. 3. `process_content_images()` joins the path to `base_dir` or accepts it directly. ...[truncated 646 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_article.py:413
Finding

Server-Side Request Forgery and Unbounded Remote Image Download

Content
View full analysis
str: """Download an external image to a temporary file and return its path.""" import tempfile url_lower = url.lower().split('?')[0] if url_lower.endswith('.png'): ext = '.png' elif url_lower.endswith('.gif'): ext = '.gif' elif url_lower.endswith('.bmp'): ext = '.bmp' else: ext = '.jpg' tmp = tempfile.NamedTemporaryFile(delete=False, suffix=ext, prefix='wechat_img_') tmp_path = tmp.name tmp.close() async with httpx.AsyncClient(timeout=30, follow_redirects=True) as client: resp = await client.get(url) resp.raise_for_status() with open(tmp_path, 'wb') as f: f.write(resp.read()) return tmp_path ``` The method is reached through attacker-influenced image sources: ```python is_url = src.startswith(('http://', 'https://')) if is_url: temp_path = await self._download_url_to_temp(src) image_path = temp_path ``` ### Technical Analysis Any HTTP or HTTPS image URL found in article content is fetched by the local process. The implementation does not validate the destination hostname or resolved IP address and follows redirects automatically. There are no controls blocking loopback, private, link-local, reserved, multicast, or cloud metadata addresses. Redirect targets are not independently validated. This permits server-side request forgery against services reachable from the machine executing the Skill. The response is also fully buffered by `resp.read()` and written to disk before the later image-upload size check is applied. A large response can therefore consume substantial memory and temporary storage. The selected filename extensi ...[truncated 1178 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_article.py:300
Finding

Configurable API Origin Can Exfiltrate WeChat Credentials, Tokens, and Publication Data

Content
View full analysis
str: """Get an access token with caching.""" if self.access_token and time.time() < self.token_expiry: return self.access_token account, base_url = self._get_account_config() url = f"{base_url}/cgi-bin/token" params = { 'grant_type': 'client_credential', 'appid': account['appId'], 'secret': account['appSecret'] } async with httpx.AsyncClient(timeout=30) as client: response = await client.get(url, params=params) result = response.json() ``` The same unvalidated `base_url` is subsequently used for image uploads and draft creation: ```python url = f"{base_url}/cgi-bin/material/add_material?access_token={access_token}&type={image_type}" ``` ```python url = f"{base_url}/cgi-bin/draft/add?access_token={access_token}" ``` ### Technical Analysis The `apiBaseUrl` configuration value is trusted without validating its scheme or hostname. The WeChat App ID and AppSecret are sent to that origin, and the returned access token is then included in upload and draft API URLs at the same origin. If the configuration file is maliciously modified, supplied from an untrusted source, or generated incorrectly, the script will send account credentials, access tokens, local images, article cont ...[truncated 1358 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish_article.py:345
Finding

Plaintext Access-Token Cache Is Created Without Enforced Restrictive Permissions

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:28
Finding

Unpinned Runtime Dependency Installation

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 8)May include surrounding context.

md
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 396)May include surrounding context.

md
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/README.md (reported line 7)May include surrounding context.

md
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/README.md (reported line 488)May include surrounding context.

md
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/publish_article.py (reported line 309)May include surrounding context.

python
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/publish_article.py (reported line 341)May include surrounding context.

python
## 功能

- 自动获取和缓存 Access Token
- 创建图文草稿到微信公众号
- 上传封面图片到素材库
- **自动上传正文图片并替换 URL**

Credential Access

High
Category
Privilege Escalation
Confidence
83% confidence
Finding

The README states that access tokens are automatically cached in a local .tokens/ directory, but provides no guidance about filesystem permissions, encryption, exclusion from backups, or multi-user host risks. On shared systems or poorly secured CI/automation environments, cached bearer tokens can be read by other local users or accidentally collected, allowing unauthorized API access until expiry.

Content

Scanner excerpt · scripts/README.md (reported line 343)May include surrounding context.

md
1. 确保公众号类型支持草稿接口(认证订阅号/服务号)
2. AppSecret 要保密,不要提交到代码仓库
3. Access Token 会自动缓存到 `.tokens/` 目录(可配置)
4. 草稿创建后需要登录公众号后台进行编辑和群发
5. 上传的图片会保存到公众号素材库,可在素材管理中查看
6. 正文图片单张不超过 2MB,封面图不超过 64MB

Credential Access

High
Category
Privilege Escalation
Confidence
81% confidence
Finding

The script persists the WeChat access token in a local JSON cache file without setting restrictive file permissions or using an OS-backed secret store. On multi-user systems or shared workspaces, another local process or user could read the token and use it to perform unauthorized API actions until expiry.

Content

Scanner excerpt · scripts/publish_article.py (reported line 353)May include surrounding context.

python
'expires_at': self.token_expiry
            }, f)

        print(f"✅ Access Token 获取成功")
        return self.access_token

    async def upload_image(self, image_path: str, is_thumb: bool = False) -> dict:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documentation describes capabilities that require file read/write and network access, but it does not declare any explicit tool scope or allowed-tools. This increases the risk of over-privileged execution, accidental data access, or silent outbound transmission because users and the host agent cannot clearly constrain what the skill is permitted to do.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The trigger examples are broad natural-language phrases that overlap with ordinary publishing requests, making unintended activation more likely. A misfire could cause article creation, file processing, image uploads, or external API calls without sufficiently explicit user intent for these actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill describes automatic image handling but does not prominently warn users that image content or referenced remote resources may be transmitted to WeChat or fetched across the network. Without explicit disclosure and consent, users may expose confidential images, tracking URLs, or internal resource references to external services.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation gives conflicting statements about whether external image URLs are automatically uploaded and replaced or left unchanged. This ambiguity is dangerous because operators may unknowingly cause third-party or sensitive image URLs to be fetched and re-uploaded to WeChat, creating privacy, compliance, and data handling risks.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The entire skill documentation, examples, prompts, and output are presented only in Chinese, with no indication that another language is supported or that the user can opt in to this locale. Per the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale constraint is clearly documented and justified.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation says external image URLs are replaced with WeChat material URLs at L067, but elsewhere it states external URLs '不会上传' and '保持不变' at L134-L138 and L397. This is an active contradiction in intent/documentation that could mislead users about whether remote content will be fetched and re-uploaded.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

In the original HTML example, the comment at L083 says an external URL will be auto-uploaded, but the processed HTML at L105 still shows the same https://example.com/pic.png URL unchanged. This is a direct contradiction within the README about actual behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The example marks an external image URL as '会自动上传' at L327, but the technical description later says external URLs remain unchanged at L397. This is an explicit documentation contradiction about what the skill does with remote images.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module description and all user-facing strings in this file are fixed in Chinese, which imposes a specific language on users without opt-in or documented locale scoping. This matches the language/locale policy violation category because the skill does not provide any language selection or justify being region-specific.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code fetches arbitrary HTTP(S) URLs found in article image tags and stores the response locally before re-uploading it to WeChat. This adds an unrestricted outbound network retrieval capability that can be abused for SSRF-style access to internal or sensitive network resources, or to exfiltrate request metadata to attacker-controlled endpoints, which exceeds a narrow 'publish local article assets' role.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Remote images embedded in content are silently downloaded and then uploaded to WeChat, causing third-party content to be transmitted across two external services without a clear warning at the trust boundary. This can leak sensitive URLs, tracking tokens, private intranet references, or copyrighted/private content that the operator may not realize is being fetched and republished.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The example value "我的公众号" is Chinese-language text embedded directly in the configuration. Because this file provides no indication that the skill is region-specific or that users may choose their own locale, it may violate the policy against forcing a specific language without opt-in.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.