Back to skill

Security audit

Wechat Image Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its image-generation purpose, but its screenshot and local-server helpers create command-execution and file-exposure risks that need review before installation.

Install only if you are comfortable running local Python scripts and browser automation from this package. Use it on trusted generated files, avoid passing arbitrary filenames to auto_screenshot.py, and do not run the server on untrusted networks unless it is changed to bind to 127.0.0.1 and serve only the intended output directory.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/auto_screenshot.py:14
Finding
Shell Command Injection Through Attacker-Controlled HTML Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/auto_screenshot.py`, lines 14–23 **Vulnerability Type**: OS command injection through unsafe shell command construction **Risk Level**: High ### Vulnerable Code ```python def take_screenshot(html_path, output_path): """Take screenshot using OpenClaw browser tool""" html_path = os.path.abspath(html_path) output_path = os.path.abspath(output_path) file_url = f"file://{html_path}" print(f"🌐 Opening browser: {file_url}") # Open in browser open_cmd = f'browser open --url "{file_url}"' result = subprocess.run(open_cmd, shell=True, capture_output=True, text=True) ``` The command-line entry point only checks whether the supplied path exists before passing it to the vulnerable function: ```python html_path = sys.argv[1] output_path = sys.argv[2] if not os.path.exists(html_path): print(f"❌ HTML file not found: {html_path}") sys.exit(1) success = take_screenshot(html_path, output_path) ``` ### Technical Analysis The HTML path is obtained directly from a command-line argument and interpolated into a shell command. The command is then executed with `shell=True`. Wrapping the value in double quotes does not make it safe. A filename can contain a double quote and shell metacharacters on supported Unix-like filesystems. An attacker who controls or can create the input filename can terminate the quoted URL argument and append another shell command. The existence check does not mitigate the issue because an attacker can create a file whose literal filename contains the required quote and shell syntax. Converting the path with `os.path.abspath()` also performs no shell escaping or validation. The screenshot command at line 36 uses a constant command string and is not independently injectable. The confirmed injection point is the dynamically constructed `open_cmd`. ### Attack Path 1. An attacker obtains the ability to create an HTML file with a specially c ...[truncated 1409 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Eliminate shell interpretation and pass each command argument directly to `subprocess.run()`: ```python result = subprocess.run( ["browser", "open", "--url", file_url], shell=False, capture_output=True, text=True, check=False, ) ``` Apply additional defense-in-depth controls: 1. Resolve the input with `Path.resolve(strict=True)`. 2. Require the source to be a regular file with an approved extension such as `.html`. 3. Restrict input files to a dedicated trusted directory: ```python allowed_root = (Path(__file__).parent.parent / "output").resolve() html_file = Path(html_path).resolve(strict=True) if not html_file.is_file() or html_file.suffix.lower() != ".html": raise ValueError("Input must be an existing HTML file") if allowed_root not in html_file.parents: raise ValueError("Input must be inside the output directory") ``` 4. Avoid building any future commands through string interpolation. 5. Return failure unless the requested screenshot is actually written and validated. 6. Add regression tests using filenames containing quotes, semicolons, command substitutions, whitespace, and newline characters. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/serve.py:15
Finding
Unauthenticated HTTP Server Exposed on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/serve.py`, lines 15–21 - `package.json`, line 35 - `README.md`, lines 73 and 145 **Vulnerability Type**: Excessive network exposure and unintended file disclosure **Risk Level**: Medium ### Vulnerable Code `scripts/serve.py`: ```python def start_server(directory): """Start a simple HTTP server""" Handler = http.server.SimpleHTTPRequestHandler Handler.directory = directory with socketserver.TCPServer(("", PORT), Handler) as httpd: print(f"✓ Server running at http://localhost:{PORT}") httpd.serve_forever() ``` `package.json`: ```json "scripts": { "serve": "cd output && python3 -m http.server 8765", "generate:cover": "python3 scripts/generate.py cover", "generate:compare": "python3 scripts/generate.py compare", "generate:chart": "python3 scripts/generate.py chart" } ``` `README.md`: ```bash cd output && python3 -m http.server 8765 ``` ```bash cd output && python3 -m http.server 8765 & sleep 1 && open http://localhost:8765/cover.html ``` ### Technical Analysis The empty host string in: ```python socketserver.TCPServer(("", PORT), Handler) ``` binds the service to all available network interfaces rather than only the loopback interface. Likewise, `python3 -m http.server 8765` binds broadly unless an explicit loopback address is supplied. The output message claiming that the server is available at `localhost` does not restrict the actual listener. Other systems that can route to the host may also connect to port 8765. There is an additional directory-isolation problem in `scripts/serve.py`. Assigning: ```python Handler.directory = directory ``` does not reliably configure the directory used by `SimpleHTTPRequestHandler` instances. Its constructor accepts a `directory` argument and can initialize the instance from the process working directory when no argument is supplied. Consequently, directly launching `scripts/serve.py` may expose the curr ...[truncated 1743 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Bind the server explicitly to loopback and pass the serving directory through the handler constructor: ```python from functools import partial def start_server(directory): """Start a loopback-only HTTP server.""" handler = partial( http.server.SimpleHTTPRequestHandler, directory=str(Path(directory).resolve()), ) with socketserver.TCPServer(("127.0.0.1", PORT), handler) as httpd: print(f"Server running at http://127.0.0.1:{PORT}") httpd.serve_forever() ``` Update the package command: ```json "serve": "python3 -m http.server 8765 --bind 127.0.0.1 --directory output" ``` Update all documented commands similarly: ```bash python3 -m http.server 8765 --bind 127.0.0.1 --directory output ``` Additional hardening measures: 1. Resolve and verify that the document root is the intended `output` directory. 2. Disable directory listing if it is unnecessary. 3. Stop the server automatically after screenshot generation rather than leaving it active indefinitely. 4. Do not use this development server for deployment or on untrusted networks. 5. If remote access is genuinely required, use a production server with authentication, TLS, explicit firewall restrictions, and a narrowly scoped document root. 6. Add a startup check that verifies the bound socket is loopback-only. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
If the underlying implementation runs a local HTTP server and serves files over localhost while claiming only to generate article images, the skill exposes undeclared network-facing behavior. Even on localhost, hidden server behavior expands attack surface, can expose local content, and undermines trust in the skill’s stated purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the underlying implementation runs a local HTTP server and serves files over localhost while claiming only to generate article images, the skill exposes undeclared network-facing behavior. Even on localhost, hidden server behavior expands attack surface, can expose local content, and undermines trust in the skill’s stated purpose.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Open in browser
    open_cmd = f'browser open --url "{file_url}"'
    result = subprocess.run(open_cmd, shell=True, capture_output=True, text=True)
    
    if result.returncode != 0:
        print(f"❌ Failed to open browser: {result.stderr}")
Confidence
97% confidence
Finding
The tool command parameter includes a file URL built from external input and is passed through a shell, allowing parameter or command manipulation. Because the skill's purpose is image creation, not arbitrary command execution, this mismatch makes the issue more dangerous: an attacker could turn a screenshot request into execution of unintended shell commands or malicious browser invocations.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Take screenshot
    screenshot_cmd = f'browser screenshot --type png --fullPage'
    result = subprocess.run(screenshot_cmd, shell=True, capture_output=True, text=True)
    
    if result.returncode != 0:
        print(f"❌ Failed to take screenshot: {result.stderr}")
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises executable workflows that invoke Python scripts, write output files, and rely on browser automation, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization and transparency gap: an agent may grant broader-than-expected shell and filesystem access when activating the skill, increasing the chance of unintended command execution or file modification.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The trigger phrases are broad enough to match ordinary image-related requests, which can cause the skill to activate unexpectedly. Over-broad activation is risky because it may invoke shell, file, or browser-capable workflows in contexts where the user only asked for general help, increasing the chance of unnecessary privileged actions.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The helper introduces shell command execution and browser automation capabilities that exceed simple image-generation logic and widen the attack surface. In the context of a skill meant to generate WeChat images, accepting local HTML and opening it in a browser can expose the environment to command injection, unsafe local-file rendering, or unintended access to local/browser resources.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Open in browser
    open_cmd = f'browser open --url "{file_url}"'
    result = subprocess.run(open_cmd, shell=True, capture_output=True, text=True)
    
    if result.returncode != 0:
        print(f"❌ Failed to open browser: {result.stderr}")
Confidence
96% confidence
Finding
This command is executed through the shell while embedding user-influenced data derived from html_path into the command string. Even though the path is wrapped in double quotes, shell metacharacters such as command substitution can still be interpreted, enabling command injection if an attacker controls the input path; this is especially risky in an automation helper that launches browser tooling.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Take screenshot
    screenshot_cmd = f'browser screenshot --type png --fullPage'
    result = subprocess.run(screenshot_cmd, shell=True, capture_output=True, text=True)
    
    if result.returncode != 0:
        print(f"❌ Failed to take screenshot: {result.stderr}")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The skill documentation and usage guidance are primarily presented in Chinese, which can impose a language constraint on users without opt-in or an explicit note that the skill is intended for a Chinese-speaking audience. Under the language/locale policy, forcing a specific language without user choice or justification is a natural-language policy concern.

Missing User Warnings

Low
Confidence
81% confidence
Finding
This markdown file instructs users to launch `python3 -m http.server 8765` and open generated HTML in a browser, which affects local system behavior and can expose files in the `output` directory over the local network depending on environment configuration. The README presents this as a routine step but does not include any warning or disclosure about the server exposure or the fact that generated files will be served via HTTP.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The trigger list is entirely Chinese and the examples center on Chinese-language usage, but the description does not state that the skill is intentionally limited to Chinese-speaking or WeChat-specific users. That can amount to an implicit language/locale constraint without user opt-in or justification.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The document declares `lang="zh-CN"`, which hard-codes Simplified Chinese as the page language. Under the policy, forcing a specific language or locale without offering user choice or documenting a justified region-specific constraint is a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The document sets `lang="zh-CN"`, which hard-codes a specific language/locale in the skill asset. Under the policy rules, locale-specific behavior should be optional or clearly justified, and this file provides neither a user opt-in nor a documented regional constraint.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The document declares `lang="zh-CN"`, which hard-codes a specific language/locale. Under the policy criteria, forcing a locale without opt-in or documented justification is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The document sets the page language to "zh-CN", and the visible chart labels are also Chinese, which indicates a fixed language/locale choice. Under the policy, forcing a specific language without user opt-in or a documented justification is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The document sets `lang="zh-CN"`, which hard-codes a specific language/locale. Under the policy criteria, forcing a locale without opt-in or clear justification can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The document sets `lang="zh-CN"`, which specifies a fixed language/locale for the skill output. Under the policy, locale constraints should either offer user opt-in or be clearly documented as necessary for a region-specific use case, neither of which is present in this file.

Vague Triggers

Low
Confidence
82% confidence
Finding
This is a manifest file, so vague-trigger review applies. The description says the skill can 'Generate beautiful images for WeChat articles' but provides no explicit trigger phrases, scope boundaries, or exclusion conditions, which could make invocation matching overly broad for generic image-generation requests.

Static analysis

No suspicious patterns detected.