Back to skill

Security audit

wechat-mp-draft-publisher

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent WeChat draft-publishing purpose, but it installs and runs unverified remote executables and exposes access tokens in ways users should review carefully.

Install only if you trust the exact CLI binary source and can tolerate WeChat and possibly GitHub credential exposure in logs or process metadata. Prefer a manually installed, pinned, checksum-verified CLI, avoid direct URL auto-download, unset GITHUB_TOKEN before use, and do not share command output because it may include the WeChat access token.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/install_mp_weixin_skill.sh:112
Finding
Unverified Remote Executable Is Automatically Downloaded and Executed<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_mp_weixin_skill.sh:112-155`; execution occurs through `scripts/publish_draft.sh:73-113, 174-181`; the personal release URL is documented at `SKILL.md:61-70` **Vulnerability Type**: Unverified remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```bash tmp_file="${OUT}.download" if [ -n "${GITHUB_TOKEN:-}" ]; then curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/octet-stream" -o "$tmp_file" "$asset_url" else curl -fsSL -H "Accept: application/octet-stream" -o "$tmp_file" "$asset_url" fi is_zip=0 if [ "${asset_url##*.}" = "zip" ]; then is_zip=1 fi if [ "$is_zip" -eq 0 ] && command -v file >/dev/null 2>&1; then if file "$tmp_file" | grep -qi 'zip archive'; then is_zip=1 fi fi if [ "$is_zip" -eq 1 ]; then if ! command -v unzip >/dev/null 2>&1; then echo "unzip is required to extract zip asset" >&2 exit 1 fi extract_dir="$(mktemp -d)" unzip -o "$tmp_file" -d "$extract_dir" >/dev/null candidate="$extract_dir/mp-weixin-skill" if [ ! -f "$candidate" ]; then candidate="$(find "$extract_dir" -type f -name 'mp-weixin-skill*' | head -n1 || true)" fi if [ -z "${candidate:-}" ] || [ ! -f "$candidate" ]; then echo "cannot find mp-weixin-skill in zip asset" >&2 rm -rf "$extract_dir" exit 1 fi chmod +x "$candidate" mv "$candidate" "$OUT" rm -rf "$extract_dir" "$tmp_file" else chmod +x "$tmp_file" mv "$tmp_file" "$OUT" fi ``` The publishing wrapper subsequently installs and invokes the downloaded executable: ```bash ensure_bin() { if [ -x "$BIN" ]; then return fi if [ -z "$GITHUB_REPO" ] && [ -z "$RELEASE_URL" ]; then err_json "executable not found: $BIN ; set --bin or configure --url/MP_WECHAT_RELEASE_URL or --repo/MP_WECHAT_GITHUB_REPO" exit 1 fi local installer="$SCRIPT_DIR/install_mp_weixin_skill.sh" if [ ! -x "$installer" ]; then err_json "inst ...[truncated 2966 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove arbitrary direct-URL installation and require an explicitly installed, trusted executable. 2. Prefer bundling auditable source code or a reproducibly built binary within the reviewed package. 3. If remote installation is essential, restrict downloads to an official allowlisted repository and HTTPS origin. 4. Pin an immutable release tag, exact asset name, and expected SHA-256 digest. Do not default to `latest`. 5. Verify the digest before extraction and before granting executable permissions. 6. Add cryptographic signature or Sigstore provenance verification using a pinned trusted identity. 7. Download to a securely created temporary directory, verify the artifact, and only then atomically install it. 8. Require explicit user confirmation before the first execution of a newly downloaded binary. 9. Execute the publisher with narrowly scoped filesystem and network access where sandboxing is available. 10. Publish the CLI source and build instructions so its credential access and network behavior can be independently audited. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install_mp_weixin_skill.sh:112
Finding
GitHub Bearer Token Is Sent to Arbitrary Direct-Download Hosts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install_mp_weixin_skill.sh:112-122` **Vulnerability Type**: Credential disclosure through unrestricted authenticated download **Risk Level**: High ### Vulnerable Code ```bash tmp_file="${OUT}.download" if [ -n "${GITHUB_TOKEN:-}" ]; then curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/octet-stream" -o "$tmp_file" "$asset_url" else curl -fsSL -H "Accept: application/octet-stream" -o "$tmp_file" "$asset_url" fi ``` The destination is initialized from an unrestricted caller-supplied URL: ```bash asset_url="$URL" if [ -z "$asset_url" ]; then if [ "$TAG" = "latest" ]; then api_url="https://api.github.com/repos/${REPO}/releases/latest" else api_url="https://api.github.com/repos/${REPO}/releases/tags/${TAG}" fi ``` ### Technical Analysis When `GITHUB_TOKEN` is present, the installer unconditionally attaches it as an `Authorization: Bearer` header to `asset_url`. In direct-URL mode, `asset_url` may point to any caller-selected host. The script performs no hostname, scheme, port, or origin validation before sending the credential. Consequently, a feature intended to authenticate GitHub downloads becomes a token-exfiltration primitive. HTTPS does not mitigate this issue when the destination itself is controlled by the attacker. ### Attack Path 1. A user or automation environment has `GITHUB_TOKEN` exported. 2. An attacker causes `--url` or `MP_WECHAT_RELEASE_URL` to reference an attacker-controlled HTTPS endpoint. 3. The publishing wrapper invokes the installer with that direct URL. 4. The installer issues a request containing `Authorization: Bearer <GITHUB_TOKEN>`. 5. The attacker’s server records the header. 6. The attacker reuses the token against GitHub APIs within the permissions and lifetime granted to it. ### Impact Assessment The attacker can obtain the complete GitHub bearer token. Resulting access depends on its scopes and repository permiss ...[truncated 302 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never attach `GITHUB_TOKEN` in direct-URL mode. 2. Only send GitHub authorization headers to explicitly allowlisted GitHub API and asset origins. 3. Parse and validate the URL before making a request; require HTTPS and reject user information, unexpected ports, and unapproved hosts. 4. Separate authenticated GitHub API retrieval from unauthenticated asset retrieval rather than sharing one generic request path. 5. Constrain redirect behavior and validate the final origin before forwarding any credential. 6. Use short-lived, narrowly scoped tokens with only the repository permissions required for release retrieval. 7. Redact authorization material from all debug output and logs. 8. Add tests confirming that non-GitHub endpoints never receive an `Authorization` header. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_draft.sh:181
Finding
WeChat Access Token Is Exposed in Standard Output, Process Arguments, and Error Messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/publish_draft.sh:61-68, 181-207, 224-231` **Vulnerability Type**: Sensitive authentication token exposure **Risk Level**: High ### Vulnerable Code The error handler incorporates the complete command arguments: ```bash run_cli() { local output if ! output="$($BIN "$@" 2>&1)"; then err_json "command failed: $BIN $* ; $output" exit 1 fi local last_line last_line="$(printf '%s\n' "$output" | awk 'NF{p=$0} END{print p}')" if [ -z "$last_line" ]; then printf '{}\n' return fi printf '%s\n' "$last_line" } ``` The access token is passed to child processes as a command-line argument: ```bash AUTH_JSON="$(run_cli getAuth)" ACCESS_TOKEN="$(extract_json_field "$AUTH_JSON" "access_token")" if [ -z "$ACCESS_TOKEN" ]; then err_json "getAuth succeeded but access_token not found in: $AUTH_JSON" exit 1 fi # 2) uploadArticleImage (optional) ARTICLE_URL="" if [ -n "$ARTICLE_IMAGE" ]; then ARTICLE_JSON="$(run_cli uploadArticleImage --token "$ACCESS_TOKEN" --path "$ARTICLE_IMAGE")" ARTICLE_URL="$(extract_json_field "$ARTICLE_JSON" "url")" fi # 3) uploadCoverImage COVER_JSON="$(run_cli uploadCoverImage --token "$ACCESS_TOKEN" --path "$COVER_IMAGE")" ``` It is also supplied to `addDraft` and deliberately returned in standard output: ```bash DRAFT_JSON="$(run_cli addDraft \ --token "$ACCESS_TOKEN" \ --title "$TITLE" \ --author "$AUTHOR" \ --content-file "$CONTENT_FILE" \ --digest "$DIGEST" \ --thumb-media-id "$THUMB_MEDIA_ID")" printf '{"access_token":"%s","article_image_url":"%s","cover_upload":%s,"thumb_media_id_used":"%s","draft":%s}\n' \ "$(escape_json "$ACCESS_TOKEN")" \ "$(escape_json "$ARTICLE_URL")" \ "$COVER_JSON" \ "$(escape_json "$THUMB_MEDIA_ID")" \ "$DRAFT_JSON" ``` ### Technical Analysis The wrapper exposes the WeChat access token through three channels: 1. It passes the token through `--token "$ACCESS_TOKEN"`, potentially making it vi ...[truncated 1618 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `access_token` from the final JSON output and update the documented output contract accordingly. 2. Never include complete command arguments in errors. Report only the operation name and a sanitized error message. 3. Redact tokens and other credentials from captured child-process output before logging it. 4. Modify the CLI contract to accept the token through stdin, a dedicated inherited file descriptor, or a permission-restricted temporary file rather than a command-line argument. 5. If a temporary secret file is unavoidable, create it with mode `0600`, store it in a secure temporary directory, and delete it through a cleanup trap. 6. Minimize token lifetime and permissions where supported. 7. Ensure CI, Agent, and application logs do not retain historical token-bearing output. 8. Add regression tests that fail if token-shaped values appear in stdout, stderr, or generated command diagnostics. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill is presented as a WeChat draft publisher, but its documented behavior includes downloading and executing binaries from arbitrary direct URLs or GitHub releases and performing local installation steps. That mismatch is dangerous because users may consent to publishing content, not to supply-chain style code retrieval and execution on their machine.

Missing User Warnings

High
Confidence
98% confidence
Finding
The auto-download mode permits fetching an executable from a direct release URL or repository release and then running it, without any warning about integrity verification or trust. This is a classic remote code execution and supply-chain risk, especially because the URL can be user-controlled or point to a compromised release asset.

Ae1

High
Category
analysis-evasion
Content
bash scripts/publish_draft.sh \
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/publish_draft.sh \
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/publish_draft.sh \
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/publish_draft.sh \
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

External Script Fetching

High
Category
Supply Chain
Content
fi

  if [ -n "${GITHUB_TOKEN:-}" ]; then
    release_json="$(curl -fsSL -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "$api_url")"
  else
    release_json="$(curl -fsSL -H "Accept: application/vnd.github+json" "$api_url")"
  fi
Confidence
91% confidence
Finding
The script fetches release metadata and later downloads an executable asset from a remote source, ultimately installing code obtained over the network without integrity or publisher verification. In the context of a skill installer, this is dangerous because compromise of the repository, release asset, redirect target, or user-supplied repo/url can lead to arbitrary code execution on the host.

Missing User Warnings

High
Confidence
98% confidence
Finding
The final JSON intentionally includes the WeChat access token, which can expose bearer credentials to calling agents, logs, shell history capture, CI output, or downstream tools that consume stdout. Anyone obtaining that token may be able to perform authenticated API actions against the WeChat account until the token expires.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill explicitly instructs use of a shell script and an executable that can download, install, chmod, unzip, and run binaries, but it declares no tool scope or permission boundary. That omission makes the skill harder to sandbox and review, increasing the chance that an agent executes shell actions beyond what a user expects.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: wechat-mp-draft-publisher
description: Publish WeChat Official Account draft articles through a packaged CLI executable that wraps WeChat API calls. Use when the user wants to publish or create a draft from local article content and images, especially when the required flow is getAuth -> uploadArticleImage -> uploadCoverImage -> addDraft.
---

# WeChat MP Draft Publisher
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill asks for article content, images, and local credentials, and indicates use of WeChat and optionally GitHub, but it does not clearly warn that these materials and secrets will be transmitted to external services. This creates a data exposure risk because users may provide sensitive unpublished content or API credentials without informed consent.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill supports automatically downloading and installing an executable from a direct URL or GitHub release and then using it in the publishing flow, but the contract does not mention any integrity verification, publisher trust validation, or pinning requirements. In a skill that processes credentials and publishes content, executing externally fetched code creates a clear supply-chain risk that could lead to credential theft, arbitrary code execution, or unauthorized publication.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The installer explicitly accepts an arbitrary --url and then downloads and installs whatever executable or zip is provided. That creates a direct arbitrary-code-install path unrelated to the narrow skill purpose, and there is no signature, checksum, allowlist, or publisher verification to ensure the binary is trusted.

External Transmission

Medium
Category
Data Exfiltration
Content
asset_url="$URL"
if [ -z "$asset_url" ]; then
  if [ "$TAG" = "latest" ]; then
    api_url="https://api.github.com/repos/${REPO}/releases/latest"
  else
    api_url="https://api.github.com/repos/${REPO}/releases/tags/${TAG}"
  fi
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
asset_url="$URL"
if [ -z "$asset_url" ]; then
  if [ "$TAG" = "latest" ]; then
    api_url="https://api.github.com/repos/${REPO}/releases/latest"
  else
    api_url="https://api.github.com/repos/${REPO}/releases/tags/${TAG}"
  fi
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script silently downloads an executable, may extract a zip, marks the result executable, and moves it into place with minimal user warning or confirmation. This increases the chance of users installing untrusted code without understanding that network retrieval and executable deployment are occurring.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script will fetch a replacement executable from a user-controlled GitHub repo, release asset, or direct URL and then execute it with the user's WeChat credentials and local file inputs. This is a classic remote code execution / supply-chain risk, made worse because no integrity verification, signature check, pinned source, or trust prompt is required before execution.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script automatically downloads and executes a CLI binary when the expected executable is absent, without any warning, confirmation, or provenance disclosure to the user. In an agent skill context, this creates stealthy execution of newly retrieved code and compounds the supply-chain risk because users may not realize network retrieval and code execution occurred.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The contract requires a plaintext credential file in the user's home directory but provides no guidance on file permissions, storage hygiene, or avoiding accidental disclosure. While this documentation alone does not exfiltrate secrets, it normalizes insecure secret handling and increases the chance that app credentials are left broadly readable, committed, or exposed through logs and support workflows.

Static analysis

No suspicious patterns detected.