Back to skill

Security audit

微信公众号内容总结

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but its WeChat fetcher can make under-scoped network requests to arbitrary URLs and optionally download unbounded remote image content.

Review before installing in environments that can reach private networks or sensitive internal services. Use it only with trusted WeChat article URLs, avoid --save-images unless needed, and explicitly ask to keep generated Markdown files if you want local copies retained.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
wechat2md.py:22
Finding

Arbitrary URL Fetch Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: wechat2md.py:22-30 and wechat2md.py:136-143
Vulnerability Type: Server-Side Request Forgery (SSRF) caused by ineffective URL validation
Risk Level: High

Vulnerable Code

python
def fetch_article(url: str) -> str:
    """抓取微信公众号文章 HTML"""
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/120.0.0.0 Safari/537.36'
    }
    response = requests.get(url, headers=headers, timeout=30)
    response.raise_for_status()
    response.encoding = 'utf-8'
    return response.text
python
    # 验证 URL
    if 'mp.weixin.qq.com' not in args.url:
        print('警告: 这可能不是微信公众号文章链接')

    print(f'正在抓取文章...')

    try:
        # 抓取文章
        html = fetch_article(args.url)

Technical Analysis

The skill accepts a user-controlled URL and passes it directly to requests.get(). The purported validation only checks whether the string mp.weixin.qq.com occurs anywhere in the URL. If it does not occur, execution is not rejected; the program merely prints a warning and still sends the request.

This does not enforce the intended destination. An attacker can directly provide an unrelated URL, or place the expected text in a URL component that does not determine the destination host. For example, it could appear in user-information, a path, or a query string while the request is sent to an attacker-selected host.

In addition, requests.get() follows HTTP redirects by default. Even if an exact initial host check were added without redirect validation, an approved URL could redirect the client to localhost, a private network address, or a cloud metadata service.

Attack Path

  1. An attacker supplies a URL targeting an internal service, localhost, cloud metadata address, or attacker-controlled redirect endpoint.

...[truncated 1199 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse the URL with urllib.parse.urlparse() and reject it unless:
    • The scheme is exactly https.
    • The normalized hostname is exactly mp.weixin.qq.com, or belongs to a narrowly documented allowlist.
    • The URL contains no unexpected user-information or malformed authority component.
  2. Resolve the hostname before connecting and reject every loopback, private, link-local, multicast, reserved, or unspecified IPv4 and IPv6 address.
  3. Disable automatic redirects with allow_redirects=False, or validate the hostname and resolved address of every redirect target before following it.
  4. Repeat address validation immediately before connection to reduce DNS rebinding risk.
  5. Enforce outbound network restrictions at the container or host level so the process cannot access internal networks or metadata endpoints.
  6. Treat validation failures as fatal errors rather than warnings.
  7. Consider limiting accepted WeChat URL paths and query parameters to the minimum forms required by the skill.

T09 · Insecure Skill Coding Practices

Warning
Location
wechat2md.py:87
Finding

Unrestricted and Unbounded Image Downloads Enable SSRF and Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: wechat2md.py:87-123
Vulnerability Type: Server-Side Request Forgery and uncontrolled resource consumption
Risk Level: Medium

Vulnerable Code

python
    # 查找所有图片链接
    img_pattern = r'!\[([^\]]*)\]\(([^)]+)\)'
    images = re.findall(img_pattern, markdown)

    if not images:
        return markdown

    # 创建 images 目录
    img_dir = os.path.join(output_dir, 'images')
    os.makedirs(img_dir, exist_ok=True)

    for idx, (alt, url) in enumerate(images, 1):
        if not url.startswith('http'):
            continue

        try:
            # 下载图片
            resp = requests.get(url, headers=headers, timeout=30)
            resp.raise_for_status()

            # 确定扩展名
            content_type = resp.headers.get('Content-Type', '')
            if 'png' in content_type:
                ext = '.png'
            elif 'gif' in content_type:
                ext = '.gif'
            elif 'webp' in content_type:
                ext = '.webp'
            else:
                ext = '.jpg'

            # 保存图片
            img_filename = f'image_{idx:02d}{ext}'
            img_path = os.path.join(img_dir, img_filename)
            with open(img_path, 'wb') as f:
                f.write(resp.content)

Technical Analysis

When --save-images is enabled, image URLs derived from remote article content are requested without validating their hostname, resolved IP address, port, or redirect destination. Checking url.startswith('http') is not an effective security boundary and accepts arbitrary HTTP and HTTPS destinations.

The implementation also accesses resp.content, which buffers the entire response in memory, and then writes the complete response to disk. It imposes no maximum image count, response-body size, aggregate download size, or trusted content-type requirement. A timeout limits how long a request may take but does not ...[truncated 1754 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict image downloads to an explicit allowlist of trusted WeChat image hostnames.
  2. Require HTTPS and validate parsed hostnames rather than using string-prefix checks.
  3. Resolve each destination and reject loopback, private, link-local, multicast, reserved, and unspecified IPv4 and IPv6 addresses.
  4. Disable redirects or validate every redirect target before following it.
  5. Use streaming downloads with stream=True and stop reading after a strict per-image byte limit.
  6. Enforce limits on the number of images and the aggregate bytes downloaded for one article.
  7. Check Content-Length when present, while still enforcing limits during streaming because the header may be absent or false.
  8. Accept only narrowly approved image media types and verify the downloaded bytes using an image decoder before saving them.
  9. Delete partial files after failures and apply filesystem quotas to the output directory.
  10. Apply outbound firewall rules as defense in depth to prevent access to internal services and metadata endpoints.
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

The skill contains an explicit shell deletion command and directs the agent to run it routinely. Even though the path is partially fixed, embedding destructive shell operations in a reusable skill increases the chance of accidental deletion, misuse through filename/path handling mistakes, or unsafe adaptation of the command in other contexts.

Content

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

重要!总结完成后必须删除生成的 .md 文件。

bash
rm -f "/home/node/.openclaw/workspace-research/skills/WeChat-mp/<文件名>.md"

除非用户明确要求保留,否则一律删除。

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The entire skill is written as a Chinese-only workflow and provides a fixed Chinese summary template, with no indication that the user can choose another language for summaries or output. This can violate language/locale policy when a skill imposes a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill instructs the agent to delete the generated article file by default unless the user explicitly asks to keep it. This creates a data-loss risk because it performs a destructive action without clear user confirmation or warning, and the file path is derived from fetched content rather than an explicitly user-reviewed artifact lifecycle.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.