Back to skill

Security audit

wechat-article-reader

Security checks for vulnerabilities and agentic risk

Overview

The skill has a clear WeChat article-reading purpose, but its URL validation can be bypassed so it may fetch non-WeChat or internal network URLs despite claiming WeChat-only behavior.

Review before installing. This skill is narrowly intended to fetch public WeChat articles and does not use cookies or credentials, but it should validate that the parsed HTTPS hostname is exactly mp.weixin.qq.com and should re-check redirects before it is trusted in environments with access to private networks or local services.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/read_wechat.py:121
Finding

Insufficient URL Validation Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/read_wechat.py, lines 29–31 and 121–123
Vulnerability Type: Server-Side Request Forgery caused by inadequate URL validation
Risk Level: Medium

Vulnerable Code

python
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=20) as resp:
    page = resp.read().decode("utf-8", errors="replace")
python
url = sys.argv[1]
use_json = "--json" in sys.argv

if "mp.weixin.qq.com" not in url:
    print("⚠️ 不是微信公众号文章链接,请提供 mp.weixin.qq.com/s/xxx 格式的链接")
    sys.exit(1)

Technical Analysis

The script intends to accept only public WeChat article URLs, but it validates the user-controlled URL using a substring test:

python
"mp.weixin.qq.com" in url

This does not establish that the parsed destination hostname is exactly mp.weixin.qq.com. An attacker can place the trusted string in a URL path, query, user-information component, or attacker-controlled hostname. For example, the following URL passes validation while targeting a local service:

text
http://127.0.0.1:8080/?source=mp.weixin.qq.com

A hostname such as mp.weixin.qq.com.attacker.example also passes the substring check. The code subsequently passes the complete untrusted value to urllib.request.urlopen, which performs the network request.

Additionally, urlopen follows HTTP redirects by default. Therefore, validating only the initial string would remain insufficient: an initially acceptable endpoint could redirect the request to localhost, a private network, or another prohibited destination.

Attack Path

  1. An attacker provides a crafted URL containing the text mp.weixin.qq.com while identifying an attacker-selected destination.
  2. The substring check at lines 121–123 accepts the URL.
  3. The URL reaches fetch_article.
  4. urllib.request.urlopen connects to the attacker-selected host.
  5. The script reads the response and attempts to parse it as an article.
  6. Pars ...[truncated 1320 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse the URL with urllib.parse.urlsplit and reject malformed values.
  2. Require the https scheme.
  3. Require the normalized hostname to be exactly mp.weixin.qq.com; do not use substring or suffix checks.
  4. Reject embedded credentials and non-default ports.
  5. Restrict accepted paths to the intended public article route, such as /s or /s/..., if compatible with supported WeChat URLs.
  6. Resolve the hostname and reject every address that is loopback, private, link-local, reserved, multicast, or unspecified for both IPv4 and IPv6.
  7. Disable automatic redirects or implement a redirect handler that validates every redirect destination using the same scheme, hostname, port, and IP-address rules.
  8. Apply response-size limits to reduce resource-exhaustion risk from untrusted HTTP responses.
  9. Return generic network errors rather than exposing detailed internal connection information.

A hostname validation baseline could use the following principle:

python
from urllib.parse import urlsplit

parsed = urlsplit(url)
if (
    parsed.scheme != "https"
    or parsed.hostname != "mp.weixin.qq.com"
    or parsed.port not in (None, 443)
    or parsed.username is not None
    or parsed.password is not None
):
    raise ValueError("Only HTTPS URLs hosted on mp.weixin.qq.com are allowed")

This must be supplemented with redirect validation and destination IP checks to prevent redirect-based SSRF and DNS-based bypasses.

Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes a Python script that performs HTTP fetching of arbitrary mp.weixin.qq.com URLs, but the manifest declares no explicit tool scope or permission boundary. This creates an authorization and governance gap: the agent may use network-capable behavior without clear least-privilege constraints, making review, policy enforcement, and user-consent handling weaker.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger text includes broad everyday phrases such as “帮我看看这篇” and references to generic WeChat article contexts, which can cause the skill to activate in situations beyond the author's intent. Over-broad activation can lead to unintended external requests, fetching untrusted content, or the wrong skill handling user input, especially in multi-skill environments.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The hostname check uses a substring test ("mp.weixin.qq.com" in url) rather than parsing and validating the actual host. An attacker can supply a URL such as https://mp.weixin.qq.com.evil.com/... or place the trusted string in userinfo/query data, causing the script to fetch an arbitrary remote host. In a skill meant to only read WeChat articles, this expands network access and creates an SSRF-style capability against unintended destinations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The HTTP header hard-codes Accept-Language: zh-CN,zh;q=0.9, which imposes a specific language/locale preference. This is a natural-language policy issue because the skill does not offer user opt-in or explain why a Chinese locale is required.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.