Back to skill

Security audit

wechat article publisher

Security checks for vulnerabilities and agentic risk

Overview

The skill largely does what it claims, but it needs review because it can affect a WeChat account, stores API secrets in a local config file, and fetches arbitrary URLs without safeguards.

Install only if you are comfortable giving it WeChat Official Account API credentials and letting it create drafts or optionally submit publications. Use dry-run first, avoid untrusted URLs, prefer local reviewed Markdown and local cover images, keep app_secret out of shared repositories, and install dependencies in an isolated environment.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_wechat.py:151
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis
Article: resp = requests.get(url, timeout=30) resp.raise_for_status() ``` ```python def upload_image_from_url(self, token: str, image_url: str) -> str: img = requests.get(image_url, timeout=self.timeout) img.raise_for_status() ``` ```python if is_url(input_value): article = extract_from_url(input_value) ``` ### Technical Analysis The script accepts an arbitrary HTTP or HTTPS URL and retrieves it using the network privileges of the host running the Skill. It does not validate the destination hostname, resolved IP address, port, or redirect target. The article extraction logic also obtains the first image URL from fetched HTML. During publication, that image can be retrieved by `upload_image_from_url`. Consequently, an attacker can trigger requests either directly through the input argument or indirectly through attacker-controlled article content. No controls prevent requests to: - Loopback addresses such as `127.0.0.1` or `::1` - Private network ranges - Link-local addresses - Cloud instance metadata services - Internal administrative services - Destinations reached after an HTTP redirect The image request is particularly useful for blind SSRF because the attacker can control the image URL embedded in an otherwise legitimate-looking article. ### Attack Path 1. An attacker provides an article URL such as `http://127.0.0.1:PORT/internal` or a URL resolving to a private address. 2. The Skill recognizes it as a URL because it begins with `http://` or `https://`. 3. `extract_from_url` issues the request from the Agent host without validating the destination. 4. The response is processed a ...[truncated 981 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish_wechat.py:210
Finding

Insufficient HTML Sanitization Preserves Potentially Active Content

Content
View full analysis
str: text = decode_escaped_unicode(text or "") if is_probably_html(text): return text return markdown.markdown( text, extensions=["extra", "tables", "nl2br", "codehilite"], output_format="html", ) ``` ```python def optimize_for_wechat_html(content_html: str, template: str = "standard") -> str: template = (template or "standard").strip().lower() soup = BeautifulSoup(content_html, "html.parser") for tag in soup.find_all(["script", "style"]): tag.decompose() ``` ### Technical Analysis When input resembles HTML, `markdown_to_html` returns it without sanitization. The later optimization phase removes only `script` and `style` elements. This blacklist is insufficient for hostile HTML. It does not remove or validate: - Inline event handlers such as `onerror`, `onload`, or `onclick` - Dangerous URI schemes such as `javascript:` or selected `data:` URLs - Forms and interactive controls - Embedded objects or other active elements - Potentially dangerous attributes - Remote resources that may track preview viewers The resulting content is written to a local HTML preview and may also be sent to WeChat. Although WeChat may perform downstream sanitization, security should not depend on behavior outside this Skill. ### Attack Path 1. An attacker supplies a local Markdown file containing raw HTML or supplies a web page whose extracted content includes malicious attributes or elements. 2. `markdown_to_html` returns HTML-looking content directly, or the fetched page content is converted into a string. 3. `optimize_for_wechat_html` removes only `script` and `style` tags. 4. Other active content and dangerous attribute ...[truncated 840 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
config.json:1
Finding

WeChat Credentials Are Intended to Be Stored in a Plaintext Project File

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
scripts/requirements.txt:1
Finding

Unpinned Runtime Dependency Installation Creates Supply-Chain Risk

Content
View full analysis
=2.31.0 beautifulsoup4>=4.12.0 markdown>=3.5.0 pyyaml>=6.0.0 Pillow>=10.0.0 ``` ```python def install_dependencies() -> None: req = Path(__file__).resolve().parent / "requirements.txt" if not req.exists(): raise RuntimeError(f"Requirement file not found: {req}") subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", str(req)]) ``` ### Technical Analysis The `--install` option invokes `pip` at runtime, while every dependency is specified with only a minimum version. There are no exact version pins, package hashes, lock file, or explicit isolation requirements. The listed dependencies are recognizable mainstream packages, and the reviewed files do not configure a suspicious package index or contain a typographical package name. However, the open-ended constraints permit future releases to be installed without review. Installation can execute package build or installation logic and changes the active Python environment. This does not establish malicious dependency behavior in the current package. It is a supply-chain hardening weakness and a reproducibility issue. ### Attack Path 1. A user invokes the documented `--install` option. 2. `pip` resolves the newest available versions satisfying the lower-bound constraints. 3. A compromised, malicious, or unexpectedly incompatible future release is selected. 4. Installation-time code may execute with the privileges of the user running the Skill. 5. The installed package can subsequently execute when imported by the publishing script. ### Impact Assessment A compromised dependency could execute arbitrary Python code with the privileges of the Skill process, access readable local files and credentials, and use the runtime's networ ...[truncated 247 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (25)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs users to supply WeChat credentials and then create drafts or publish content via WeChat APIs, but it does not explicitly warn that article content and authentication secrets will be transmitted to a third-party service. This omission can mislead users about privacy, data handling, and operational consequences, increasing the chance of unintended disclosure or account-affecting actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The --publish option performs a live submission to the WeChat publishing endpoint, which can affect the user's official account, yet the skill presents it as an optional step without a strong warning about its real-world impact. Users may trigger account-side publication actions without fully understanding that this is not a local preview and may create externally visible operational changes.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill instructs the agent to read local files, invoke shell commands, write or edit configuration, and access remote URLs and WeChat APIs, but it declares no explicit tool scope or allowed-tools boundary. This creates an over-privileged execution model where an agent may use broader file, network, or shell capabilities than the task strictly requires, increasing the risk of unintended file access, SSRF-like URL fetching, or command misuse when handling untrusted input.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The default prompt triggers the skill for any request to publish a Markdown file or URL to WeChat, without meaningful constraints on source trust, user confirmation, or publication scope. In a publishing automation skill, this broad invocation increases the chance of unintended activation and could lead to accidental or attacker-influenced content being drafted or submitted for publication.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 29)May include surrounding context.

python
yaml = None
    BeautifulSoup = None

TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
DRAFT_ADD_URL = "https://api.weixin.qq.com/cgi-bin/draft/add"
PUBLISH_SUBMIT_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit"
PUBLISH_GET_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/get"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 30)May include surrounding context.

python
yaml = None
    BeautifulSoup = None

TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
DRAFT_ADD_URL = "https://api.weixin.qq.com/cgi-bin/draft/add"
PUBLISH_SUBMIT_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit"
PUBLISH_GET_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/get"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 31)May include surrounding context.

python
yaml = None
    BeautifulSoup = None

TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
DRAFT_ADD_URL = "https://api.weixin.qq.com/cgi-bin/draft/add"
PUBLISH_SUBMIT_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit"
PUBLISH_GET_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/get"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 32)May include surrounding context.

python
yaml = None
    BeautifulSoup = None

TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
DRAFT_ADD_URL = "https://api.weixin.qq.com/cgi-bin/draft/add"
PUBLISH_SUBMIT_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit"
PUBLISH_GET_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/get"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 33)May include surrounding context.

python
yaml = None
    BeautifulSoup = None

TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token"
DRAFT_ADD_URL = "https://api.weixin.qq.com/cgi-bin/draft/add"
PUBLISH_SUBMIT_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/submit"
PUBLISH_GET_URL = "https://api.weixin.qq.com/cgi-bin/freepublish/get"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Several user-facing strings are written only in Chinese, such as the dependency-install error here and similar messages elsewhere, but the script does not offer a language or locale choice. This can violate a language/locale policy when a skill forces one language without user opt-in.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Bundling a self-install capability that runs pip install inside the skill expands the trust boundary from article publishing to package management. Even though it appears convenience-motivated, it enables execution of third-party package installation logic and increases supply-chain exposure beyond the core skill purpose.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
93% confidence
Finding

The script includes a self-install path that executes pip via subprocess.check_call. Although the command is not shell-injected and uses a fixed argument list, it still performs code installation/execution from a requirements.txt file at runtime, which can introduce supply-chain risk or unexpected package execution if the dependency file or package sources are tampered with.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 60)May include surrounding context.

python
req = Path(__file__).resolve().parent / "requirements.txt"
    if not req.exists():
        raise RuntimeError(f"未找到依赖文件: {req}")
    subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", str(req)])


def slugify(text: str) -> str:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

When the input is a URL, the script downloads the remote page content, and later submits the processed article content to WeChat via draft/publish API calls. Although publishing is the script's purpose, there is no clear user-facing notice in the execution path that remote content and associated metadata will be sent over the network to external services.

Content

No source excerpt is available for this finding.

Tainted flow: 'files' from requests.get (line 568, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 549)May include surrounding context.

python
def upload_image_from_path(self, token: str, image_path: Path) -> str:
        with image_path.open("rb") as fh:
            files = {"media": (image_path.name, fh, "image/jpeg")}
            resp = requests.post(
                MATERIAL_ADD_URL,
                params={"access_token": token, "type": "image"},
                files=files,

Tainted flow: 'files' from requests.get (line 568, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The code fetches image content from an attacker-controlled URL and immediately re-uploads the bytes to WeChat without validating origin, size, type, or safety. This creates an SSRF-style data relay/exfiltration primitive and can be abused to make the host retrieve internal or sensitive resources and forward them to an external service.

Content

Scanner excerpt · scripts/publish_wechat.py (reported line 569)May include surrounding context.

python
img.raise_for_status()
        content_type = img.headers.get("Content-Type", "image/jpeg")
        files = {"media": ("thumb.jpg", img.content, content_type)}
        resp = requests.post(
            MATERIAL_ADD_URL,
            params={"access_token": token, "type": "image"},
            files=files,

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script always writes a preview HTML file to the current working directory before checking --dry-run, but there is no confirmation prompt or explicit user-facing disclosure immediately around this file write. Because this is a code file, file writes that affect the user's filesystem should have some visible warning unless clearly disclosed.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower bound only, so builds may resolve to different versions over time and may inadvertently install a vulnerable or incompatible release. In a publishing skill that fetches remote content, dependency drift increases supply-chain and reliability risk, though this file alone does not prove active exploitation.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
markdown>=3.5.0
pyyaml>=6.0.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The manifest does not pin requests, so it is impossible to verify whether the installed version is affected by known advisories such as credential leaks or other historical flaws. In a skill that fetches remote URLs, this uncertainty matters because a vulnerable HTTP client could expose secrets or mishandle attacker-controlled responses.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

beautifulsoup4 is unpinned, which makes installations non-reproducible and can pull in future releases with security or parsing regressions. Because this skill processes web content, parser behavior changes could affect safety and integrity, but the issue remains a low-severity supply-chain weakness.

Content

Scanner excerpt · scripts/requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
markdown>=3.5.0
pyyaml>=6.0.0
Pillow>=10.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

Using markdown>=3.5.0 without an exact pin allows uncontrolled version selection, including releases later found vulnerable or unstable. Since the skill converts article content, unsafe or buggy parser behavior could affect availability or content handling, even if this manifest alone does not show direct exploit code.

Content

Scanner excerpt · scripts/requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
markdown>=3.5.0
pyyaml>=6.0.0
Pillow>=10.0.0

Unverifiable Dependency: markdown has 2 known advisory(ies) (CVE-2025-69534 (Python-Markdown has an Uncaught Exception); CVE-2025-69534 (Python-Markdown version 3.8 contain a vulnerability where malformed HTML-like se)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding

Because markdown is not pinned, the environment may install a version impacted by known advisories, and the manifest provides no way to verify safety. This is especially relevant for a content-publication skill because malformed attacker-supplied content could trigger parser bugs or denial-of-service conditions.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

PyYAML is unpinned, creating uncertainty about which version is actually installed and whether it contains known flaws. Given PyYAML's history of unsafe parsing issues, failing to pin versions increases risk if the skill later parses untrusted YAML input or inherits a bad release.

Content

Scanner excerpt · scripts/requirements.txt (reported line 4)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
markdown>=3.5.0
pyyaml>=6.0.0
Pillow>=10.0.0

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
94% confidence
Finding

PyYAML has a history of serious parsing and deserialization issues, and without a pinned version the actual installed release cannot be validated against known CVEs. If this skill consumes YAML configuration or content from untrusted sources, a vulnerable version could increase the attack surface significantly.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

Pillow is declared with a minimum version only, so installations may resolve to vulnerable or behaviorally different versions. Because image processing libraries have a long history of parser bugs and resource-consumption issues, leaving the version floating is a low-severity but real supply-chain risk.

Content

Scanner excerpt · scripts/requirements.txt (reported line 5)May include surrounding context.

text
beautifulsoup4>=4.12.0
markdown>=3.5.0
pyyaml>=6.0.0
Pillow>=10.0.0

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

Pillow is unpinned despite multiple historical advisories, so the manifest cannot guarantee the installed version is patched. Since this skill may process article images, an attacker-controlled image could potentially trigger vulnerable parsing or resource-exhaustion behavior if an affected version is resolved.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.