Back to skill

Security audit

WeChat Publisher With WeMD Render

Security checks for vulnerabilities and agentic risk

Overview

This WeChat publishing skill is mostly coherent for its purpose, but it automatically executes mutable third-party setup code and can upload broad local file paths, so it should be reviewed carefully before installation.

Install only if you are comfortable giving the skill access to a WeChat account, local article/image files, and first-use Node/Git/npm setup. Prefer running it in a constrained workspace with only intended article assets and dedicated WeChat credentials, and avoid storing unrelated secrets in /root/.openclaw/.env before use.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/setup.py:67
Finding

Mutable Third-Party Source and Packages Are Downloaded and Executed Automatically

Content
View full analysis
str | None: if NODE_MODULES.is_dir(): return None r = subprocess.run( ["npm", "install", "--omit=dev", "--no-audit", "--no-fund"], cwd=str(WEMD_DIR), capture_output=True, text=True, timeout=120, ) if r.returncode != 0: return f"npm install failed: {r.stderr.strip()[:300]}" _patch_markdown_it() return None def _build_core() -> str | None: if CORE_DIST.is_dir() and any(CORE_DIST.iterdir()): return None with tempfile.TemporaryDirectory() as tmpdir: tmp = Path(tmpdir) r = subprocess.run( ["git", "clone", "--depth", "1", WEMD_REPO, str(tmp / "WeMD")], capture_output=True, text=True, timeout=60, ) if r.returncode != 0: return f"git clone failed: {r.stderr.strip()[:300]}" core_dir = tmp / "WeMD" / "packages" / "core" r = subprocess.run( ["npm", "install", "--no-audit", "--no-fund"], cwd=str(core_dir), capture_output=True, text=True, timeout=120, ) if r.returncode != 0: return f"core npm install failed: {r.stderr.strip()[:300]}" r = subprocess.run( ["npx", "--yes", "typescript", "--build"], cwd=str(core_dir), capture_output=True, text=True, timeout=60, ) if r.returncode != 0: tsc_bin = core_dir / "node_modules" / ".bin" / "tsc" r = subprocess.run( [str(tsc_bin)] if tsc_bin.is_file() else ["npx", "tsc"], cwd=str(core_dir), capture_output=True, text=True, timeout=60, ) ``` The bundled package manifest also uses ...[truncated 3012 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/manage_themes.py:17
Finding

Unvalidated Theme Identifier Enables Path Traversal and File Modification

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/wechat_client.py:55
Finding

Unrestricted Local File Paths Can Be Uploaded to the WeChat API

Content
View full analysis
dict: path = Path(file_path).expanduser().resolve() if not path.is_file(): raise WeChatSkillError(f"File not found: {file_path}") boundary = f"----OpenClawBoundary{uuid.uuid4().hex}" mime_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream" body = bytearray() for name, value in (extra_fields or {}).items(): body.extend(f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n".encode()) body.extend(f"--{boundary}\r\nContent-Disposition: form-data; name=\"{file_field}\"; filename=\"{path.name}\"\r\nContent-Type: {mime_type}\r\n\r\n".encode()) body.extend(path.read_bytes()) body.extend(f"\r\n--{boundary}--\r\n".encode()) req = urllib.request.Request(url, data=bytes(body), method="POST", headers={"Accept": "application/json", "Content-Type": f"multipart/form-data; boundary={boundary}"}) with urllib.request.urlopen(req, timeout=60) as resp: raw = resp.read().decode("utf-8") ``` The standalone material uploader accepts any path: ```python parser = argparse.ArgumentParser() parser.add_argument("--file", required=True) parser.add_argument("--type", default="thumb", choices=["image", "voice", "video", "thumb"]) args = parser.parse_args() try: result = upload_permanent_material(args.file, material_type=args.type) ``` Inline image resolution also permits absolute paths and paths that escape the selected base directory: ```python candidate = Path(src).expanduser() if not candidate.is_absolute(): candidate = base_dir / candidate candid ...[truncated 2441 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/wechat_client.py:16
Finding

Credential Loader Imports Every Variable from a Global Agent Environment File

Content
View full analysis
None: for env_path in [Path("/root/.openclaw/.env"), Path(__file__).resolve().parents[1] / ".env"]: if not env_path.exists(): continue for raw_line in env_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'")) def require_env(name: str) -> str: value = os.getenv(name) if not value: raise WeChatSkillError(f"Missing env: {name}") return value ``` Only two values are needed later: ```python url = build_api_url( "/cgi-bin/token", grant_type="client_credential", appid=require_env("WECHAT_APP_ID"), secret=require_env("WECHAT_APP_SECRET"), ) ``` ### Technical Analysis The WeChat client requires only `WECHAT_APP_ID` and `WECHAT_APP_SECRET`, but `load_dotenv()` reads every assignment from the global `/root/.openclaw/.env` file and inserts each value into the process environment. Environment variables are normally inherited by child processes. The Skill subsequently launches Node.js, npm, git, npx, and the renderer without passing a restricted `env` mapping. As a result, unrelated credentials imported from the global Agent configuration may become visible to third-party build tools and renderer dependencies. No direct code path was found that intentionally transmits unrelated variables to an external host. The risk arises from unnecessarily broad secret exposure combined with automatic execution of external dependencies. ### Attack Path 1. The global OpenClaw environment file contains credentials for WeChat and unr ...[truncated 1017 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (52)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill description and workflow claim safety-critical behavior such as mandatory human confirmation before publishing, but this file provides only documentation-level assurances rather than enforceable controls. If the underlying implementation does not strictly enforce confirmation and operation limits, an agent could publish or perform destructive actions based on prompt interpretation alone, creating a confused-deputy risk and unauthorized content changes.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill description and workflow claim safety-critical behavior such as mandatory human confirmation before publishing, but this file provides only documentation-level assurances rather than enforceable controls. If the underlying implementation does not strictly enforce confirmation and operation limits, an agent could publish or perform destructive actions based on prompt interpretation alone, creating a confused-deputy risk and unauthorized content changes.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
74% confidence
Finding

The code automatically reads secrets from fixed .env file locations, including /root/.openclaw/.env, without any scoping or provenance checks. In an agent environment, this can widen access to credentials beyond the minimum necessary and makes secret consumption implicit, which increases the risk of unauthorized third-party API use if this skill is invoked unexpectedly.

Content

Scanner excerpt · scripts/wechat_client.py (reported line 18)May include surrounding context.

python
def load_dotenv() -> None:
    for env_path in [Path("/root/.openclaw/.env"), Path(__file__).resolve().parents[1] / ".env"]:
        if not env_path.exists():
            continue
        for raw_line in env_path.read_text(encoding="utf-8").splitlines():

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill advertises and instructs use of scripts that imply shell execution, filesystem access, network calls to WeChat APIs, and likely environment-variable credential use, but it declares no explicit tool scope or permission boundaries. In an agent setting, this increases the risk of unintended command execution, broad file access, and external data exfiltration because the runtime is not constrained to the minimum necessary capabilities.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The markdown content is presented entirely in Chinese, including section headings, capability descriptions, and error explanations, with no indication that language selection is optional. Under the stated policy, forcing a specific language without user opt-in is a natural-language locale violation unless clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The safety rules are written entirely in Chinese, which imposes a specific language on readers and operators without any visible opt-in or statement that the skill is region-specific. Under the policy, forcing a language or locale without user choice is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code writes a new CSS file and updates a JSON registry when adding a theme, and deletes a CSS file plus updates the registry when deleting a theme. Although these operations are central to theme management, the file contains no confirmation prompt, warning comment/docstring, or user-facing disclosure that local files will be created, modified, or removed.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

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

python
from setup import ensure_installed
    ensure_installed()
    payload_str = json.dumps({"markdown": markdown_text, "theme": theme}, ensure_ascii=False)
    r = subprocess.run(
        ["node", str(WEMD_RENDER_JS), "--json"],
        input=payload_str, capture_output=True, text=True, timeout=30,
    )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code automatically uploads local inline images to a remote WeChat endpoint during normalization, based on article HTML content, without any confirmation gate in this file. In an agent setting, that can exfiltrate local files referenced as paths if an untrusted prompt or document causes the agent to process attacker-influenced content.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

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

python
def _check_cmd(cmd: str) -> bool:
    try:
        r = subprocess.run([cmd, "--version"], capture_output=True, text=True, timeout=5)
        return r.returncode == 0
    except Exception:
        return False

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
93% confidence
Finding

This code automatically runs npm install in the local vendor directory, which can execute arbitrary lifecycle scripts from dependencies during setup. Because installation happens implicitly and without user confirmation, a compromised dependency, lockfile, or package configuration could trigger remote code execution on the host.

Content

Scanner excerpt · scripts/setup.py (reported line 70)May include surrounding context.

python
def _install_npm() -> str | None:
    if NODE_MODULES.is_dir():
        return None
    r = subprocess.run(
        ["npm", "install", "--omit=dev", "--no-audit", "--no-fund"],
        cwd=str(WEMD_DIR), capture_output=True, text=True, timeout=120,
    )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script initiates network access and package installation automatically, without any user-facing warning, prompt, or explicit opt-in. In an agent skill context, hidden side effects are dangerous because invoking the skill can unexpectedly modify the host, consume network access, and trigger dependency execution.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The script performs an unauthenticated git clone from a remote repository at runtime and then later builds code from that checkout. This creates a supply-chain execution path where a compromised upstream repo, DNS/TLS interception in a hostile environment, or unexpected repo changes can introduce malicious code into the build process.

Content

Scanner excerpt · scripts/setup.py (reported line 86)May include surrounding context.

python
with tempfile.TemporaryDirectory() as tmpdir:
        tmp = Path(tmpdir)
        r = subprocess.run(
            ["git", "clone", "--depth", "1", WEMD_REPO, str(tmp / "WeMD")],
            capture_output=True, text=True, timeout=60,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

Running npm install inside the freshly cloned remote repository can execute arbitrary preinstall, install, or postinstall scripts from that repository's dependency graph. Combined with the preceding runtime clone, this is a strong supply-chain remote code execution risk.

Content

Scanner excerpt · scripts/setup.py (reported line 94)May include surrounding context.

python
return f"git clone failed: {r.stderr.strip()[:300]}"

        core_dir = tmp / "WeMD" / "packages" / "core"
        r = subprocess.run(
            ["npm", "install", "--no-audit", "--no-fund"],
            cwd=str(core_dir), capture_output=True, text=True, timeout=120,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
90% confidence
Finding

Invoking npx --yes typescript --build may fetch and execute packages based on the repository state and local environment, extending the supply-chain risk beyond the initial clone/install. This is especially risky because it happens automatically as part of setup and compiles code from an unpinned remote checkout.

Content

Scanner excerpt · scripts/setup.py (reported line 101)May include surrounding context.

python
if r.returncode != 0:
            return f"core npm install failed: {r.stderr.strip()[:300]}"

        r = subprocess.run(
            ["npx", "--yes", "typescript", "--build"],
            cwd=str(core_dir), capture_output=True, text=True, timeout=60,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
82% confidence
Finding

The fallback execution of node_modules/.bin/tsc or npx tsc runs binaries from the cloned repository's dependency tree or from package resolution, both of which may be attacker-controlled if the upstream source is compromised. While expected in a build workflow, doing this automatically inside a user-facing skill setup increases the chance of unintended code execution.

Content

Scanner excerpt · scripts/setup.py (reported line 107)May include surrounding context.

python
)
        if r.returncode != 0:
            tsc_bin = core_dir / "node_modules" / ".bin" / "tsc"
            r = subprocess.run(
                [str(tsc_bin)] if tsc_bin.is_file() else ["npx", "tsc"],
                cwd=str(core_dir), capture_output=True, text=True, timeout=60,
            )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code deletes and replaces CORE_DIST with shutil.rmtree(CORE_DIST) and copytree(...) without warning or backup. If the directory contents were locally modified, relied upon by other components, or symlink-manipulated in a hostile environment, this can lead to data loss or unintended file replacement.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code performs a network operation that transmits a local file to WeChat via upload_permanent_material, but the script provides no prompt, warning, or explanatory message before doing so. The only visible text is a brief Chinese module docstring, which does not serve as a runtime disclosure to the user about data transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This script uploads media to WeChat at L17 and later creates or updates remote drafts at L64 and L70, which transmits article content and file data over the network. The file contains no confirmation prompt, user-facing print/log message, or comment/docstring warning that local content and image paths will be sent to an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The multipart upload helper reads arbitrary local files and transmits their contents to the WeChat API, but the function includes no confirmation prompt, logging, or explanatory comment/docstring to disclose this behavior. Because this code handles user-supplied file paths and sends file data off-system, it matches the missing-warning criterion for network transmission of user data in a code file.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest description enumerates creation, update, preview, publish confirmation, status queries, and querying materials/drafts/published articles, but it does not mention destructive operations. This file implements delete_draft, delete_published_article, and delete_material, which allow remote content deletion in the WeChat account and materially expand the behavior beyond the stated skill description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The module docstring is written only in Chinese and provides no indication that language is selectable or contextually required. Under the stated policy, forcing a specific language without opt-in is a natural-language locale violation unless the locale restriction is explicitly justified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The code invokes an external node process to render content, but this file provides no confirmation prompt, print/log statement, or explanatory warning around that execution. Because this is a code file, shell/subprocess execution should have some visible disclosure unless it is clearly communicated elsewhere.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The natural-language documentation in the file is written only in Chinese and does not offer any language choice or indicate this is a region-specific skill. That can violate language/locale policy when users are not given an explicit opt-in or alternative.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The only natural-language description in the file is the Chinese docstring 上传永久素材到微信, which fixes the skill description to a specific language with no indication of user opt-in or locale-specific justification. Under the policy, forcing a language or locale without choice can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.