Back to skill

Security audit

Amazon Listing Factory

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its Amazon listing and optional image-generation purpose, but it also has under-disclosed credential, logging, recipient-discovery, and outbound messaging behavior that users should review carefully.

Install only if you are comfortable with product details, prompts, image URLs, and configured API credentials being sent to external model/image services, and review the Feishu recipient-routing behavior before enabling image generation. Use tightly scoped keys, avoid shared or writable skill directories, do not put untrusted content in .env, and rotate any image workflow key if this version has already run with logging enabled.

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

T09 · Insecure Skill Coding Practices

Error
Location
run.sh:7
Finding
Arbitrary Code Execution Through Shell-Sourced Credential File<![CDATA[ ## Vulnerability Details **File Location**: `run.sh:7-11` **Vulnerability Type**: Shell execution through an untrusted configuration file **Risk Level**: High ### Vulnerable Code ```bash if [ -f "$SKILL_DIR/.env" ]; then set -a # shellcheck disable=SC1091 source "$SKILL_DIR/.env" set +a fi ``` ### Technical Analysis The `.env` file is loaded with Bash's `source` command. This does not parse the file as passive configuration; it executes every line as shell code with the privileges of the user running the Skill. A valid environment file only needs static `KEY=VALUE` assignments. Using `source` unnecessarily expands the trusted computing boundary and permits command substitutions, shell functions, redirections, and arbitrary commands. For example, a malicious `.env` could contain: ```bash LISTING_API_KEY="$(malicious-command)" ``` or direct shell commands unrelated to configuration. This behavior exceeds the minimum privileges needed to load API settings. Although the audited template itself contains only static assignments, exploitation becomes possible if another account, compromised installation process, archive extraction, synchronization mechanism, or local process can alter `.env`. ### Attack Path 1. An attacker gains the ability to modify or replace the Skill's `.env` file. 2. The attacker inserts shell commands or command substitutions into the file. 3. A user invokes `run.sh`. 4. Bash executes the attacker-controlled content through `source`. 5. The commands inherit the invoking user's filesystem access, environment variables, network access, and other available privileges. ### Impact Assessment Successful exploitation provides arbitrary command execution as the user running OpenClaw or the Skill. The attacker could read accessible credentials, alter workspace files, execute network requests, or compromise other resources available to that account. This code does not itself elevate privileges to root, so the ultimate scope re ...[truncated 53 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not use `source`, `.`, `eval`, or command substitution to load credential files. - Parse the file as data with a strict allowlist of expected names: - `LISTING_API_KEY` - `LISTING_BASE_URL` - `LISTING_MODEL` - `COZE_TOKEN` - `COZE_WORKFLOW_ID` - `COZE_API_URL` - `MIHE_KEY` - Reject malformed lines, duplicate keys, shell metacharacters, command substitutions, and unexpected variable names. - Prefer loading the file inside Python using the existing non-executing parser. - Require restrictive permissions such as `0600` and verify that the file is owned by the expected user before reading it. - Avoid automatically trusting configuration files copied or modified by other processes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
modules/coze_generate_image.py:34
Finding
Image Workflow Credential and User Metadata Persisted in Plaintext Logs<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:454-459`, `modules/coze_generate_image.py:27`, `modules/coze_generate_image.py:34-39`, and `modules/coze_generate_image.py:528-540` **Vulnerability Type**: Plaintext logging of credentials and user identifiers **Risk Level**: High ### Vulnerable Code The parent process embeds the image workflow key in a serialized subprocess argument: ```python payload = { "prompt": prompt_en, "image_urls": image_urls, "key": os.environ.get("MIHE_KEY", "").strip() or file_env.get("MIHE_KEY", "").strip() or os.environ.get("COZE_IMAGE_WORKFLOW_KEY", "").strip() or file_env.get("COZE_IMAGE_WORKFLOW_KEY", "").strip(), "link_only": True, "label": label, } ``` The image module defines a persistent log in the Skill directory and writes arbitrary payloads without redaction: ```python RUN_LOG_PATH = str(Path(__file__).resolve().parent / "runtime.log") def append_runtime_log(payload: dict): try: from datetime import datetime line = {"ts": datetime.now().isoformat(timespec="seconds"), **payload} with open(RUN_LOG_PATH, "a", encoding="utf-8") as f: f.write(json.dumps(line, ensure_ascii=False) + "\n") except Exception: pass ``` After image generation, it logs the original subprocess argument and communication metadata: ```python append_runtime_log({ "user_input": user_input, "prompt": parsed.get("prompt"), "label": parsed.get("label", ""), "receive_id": receive_id, "receive_id_type": receive_id_type, "receive_target_source": receive_target_source, "chat_id": parsed.get("chat_id", ""), "chat_type": parsed.get("chat_type", ""), "link_only": link_only, "image_url": image_url, }) ``` ### Technical Analysis `skill.py` serializes `MIHE_KEY` or `COZE_IMAGE_WORKFLOW_KEY` into the JSON command-line argument passed to the image module. In the normal parent-driven image-generation flow, the image module receives that e ...[truncated 1930 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never include API keys or workflow keys in `user_input`, logs, error messages, or command-line arguments. - Pass the workflow key through a narrowly scoped environment variable or another protected inter-process channel. - Log only an invocation identifier, status, duration, and non-sensitive label when operational logging is necessary. - Explicitly redact fields named `key`, `token`, `secret`, `authorization`, and similar variants before serialization. - Avoid logging raw prompts, recipient IDs, chat IDs, reference URLs, and generated URLs unless the user explicitly enables diagnostic logging. - Create any required log with mode `0600`, verify ownership, and place it outside distributable or synchronized source directories. - Add bounded rotation and a documented short retention period. - Remove existing `runtime.log` files securely and rotate any credential that may already have been recorded. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
modules/coze_generate_image.py:134
Finding
OpenClaw Terminal Pane Scraping to Discover Feishu User and Chat Identifiers<![CDATA[ ## Vulnerability Details **File Location**: `modules/coze_generate_image.py:134-169` **Vulnerability Type**: Access beyond the least privilege required for image generation **Risk Level**: Medium ### Vulnerable Code ```python def detect_runtime_receive_target_from_tmux(): try: proc = subprocess.run( ["tmux", "capture-pane", "-pt", "openclaw", "-S", "-300"], capture_output=True, text=True, timeout=10, ) if proc.returncode != 0: return {"receive_id": "", "receive_id_type": "", "source": "", "chat_id": "", "chat_type": ""} text = proc.stdout or "" lines = text.splitlines() pattern = re.compile( r"received message from (ou_[A-Za-z0-9]+) in (oc_[A-Za-z0-9]+) \(([^)]+)\)" ) for line in reversed(lines): m = pattern.search(line) if m: sender_open_id = m.group(1) chat_id = m.group(2) chat_type = m.group(3) if sender_open_id: return { "receive_id": sender_open_id, "receive_id_type": "open_id", "source": "tmux:openclaw", "chat_id": chat_id, "chat_type": chat_type, } return {"receive_id": "", "receive_id_type": "", "source": "", "chat_id": "", "chat_type": ""} except Exception: return {"receive_id": "", "receive_id_type": "", "source": "", "chat_id": "", "chat_type": ""} ``` ### Technical Analysis If a recipient cannot be found in environment variables, the Skill captures the last 300 lines of the `openclaw` tmux pane and searches those lines for Feishu sender and chat identifiers. Reading another application's terminal history is not necessary to generate Amazon listing text or images. It crosses a least-privilege boundary because the captured pane may co ...[truncated 1854 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove tmux pane capture and terminal-history parsing entirely. - Require the host integration to pass a recipient identifier explicitly in a structured invocation context. - Bind recipient data to the current request using an invocation ID or authenticated host-provided metadata. - Validate `receive_id_type` against a strict allowlist and validate each identifier's expected format. - For the listing Skill's normal `link_only` mode, skip recipient discovery completely. - Do not store recipient or chat identifiers in persistent logs. - If no verified recipient is available, return the image link to the caller rather than guessing a destination. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
skill.py:213
Finding
API Credentials and User Data Can Be Sent to Unrestricted Configurable Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:200-220`, `skill.py:247-280`, `modules/coze_generate_image.py:14-16`, and `modules/coze_generate_image.py:359-380` **Vulnerability Type**: Missing endpoint and transport validation for authenticated network requests **Risk Level**: High ### Vulnerable Code The listing provider URL can be supplied directly through environment variables or the `.env` file: ```python base_url = ( os.environ.get("LISTING_BASE_URL", "").strip() or os.environ.get("OPENAI_BASE_URL", "").strip() or os.environ.get("MOONSHOT_BASE_URL", "").strip() or file_env.get("LISTING_BASE_URL", "").strip() or file_env.get("OPENAI_BASE_URL", "").strip() or file_env.get("MOONSHOT_BASE_URL", "").strip() or "https://api.openai.com/v1" ).rstrip("/") ``` The configured listing API key and message payload are then sent to that destination: ```python payload = {"model": cfg["model"], "messages": messages} if response_json: payload["response_format"] = {"type": "json_object"} url = f'{cfg["base_url"]}/chat/completions' req = urllib.request.Request( url, data=json.dumps(payload, ensure_ascii=False).encode("utf-8"), method="POST", headers={"Authorization": f'Bearer {cfg["api_key"]}', "Content-Type": "application/json"}, ) last_error = None for attempt in range(3): try: with urllib.request.urlopen(req, timeout=180) as resp: data = json.loads(resp.read().decode("utf-8")) ``` The image workflow destination is likewise unrestricted: ```python WORKFLOW_ID = os.environ.get("COZE_WORKFLOW_ID", "").strip() COZE_TOKEN = os.environ.get("COZE_TOKEN", "").strip() COZE_API_URL = os.environ.get("COZE_API_URL", "https://api.coze.cn/v1/workflow/run").strip() ``` The Coze bearer token is sent to the configured URL: ```python def call_coze_once(parameters: dict): body = { "workflow_id": WORKFLOW_ID, "parameters": parameters, "connector_id": DEFAULT_CONNECTOR ...[truncated 3065 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require `https` for all remote API destinations and reject plaintext HTTP. - Parse URLs with a standard URL parser and reject: - Embedded usernames or passwords - Unsupported schemes - Unexpected ports - Loopback, link-local, private-network, and metadata-service destinations unless explicitly required - Use an allowlist of approved provider hostnames by default, such as the documented OpenAI or Coze API hosts. - If custom compatible providers must be supported, require explicit administrative approval and display the exact credential destination before first use. - Maintain separate credentials for separate providers; do not automatically send a general OpenAI key to an arbitrary compatible endpoint. - Disable or tightly control redirects for authenticated requests, especially redirects to a different origin. - Apply least-privilege scopes, spending limits, and rotation policies to all provider credentials. - Do not place `MIHE_KEY` inside a request sent to an unverified workflow host. - Document clearly that product text, prompts, and reference-image URLs leave the local system when generation is requested. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (36)

Tainted flow: 'req' from os.environ.get (line 370, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
}
    )

    with urllib.request.urlopen(req, timeout=120) as resp:
        return json.loads(resp.read().decode("utf-8"))
Confidence
96% confidence
Finding
The destination URL and bearer token are environment-controlled, and user prompt data is transmitted to that endpoint without in-code trust restrictions. If the environment is misconfigured or attacker-controlled, prompts and workflow data can be exfiltrated to an arbitrary external service along with sensitive credentials or metadata.

Credential Access

High
Category
Privilege Escalation
Content
bash ~/.openclaw/workspace/skills/amazon-listing-factory/run.sh "生成listing:充电宝,美国站,突出便携、大容量、安全感,输出6张图"

如需自动生图,请先配置 .env 中的图片环境变量,并前往米核获取 KEY:
miheai.com/s/98707
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documented purpose is Amazon listing generation, but the observed behavior includes external API calls, image generation, recipient identification via environment/tmux inspection, outbound delivery to Feishu, and runtime logging. This hidden functionality creates a serious trust boundary violation: users may provide commercial content or sensitive data for copywriting while the skill silently performs networked side effects and user-context discovery unrelated to the stated task.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The code enumerates many environment variables and even inspects tmux session output to infer current messaging recipients and chat context. For an Amazon listing draft skill, this is unjustified access to operational/user metadata and enables unintended message targeting, privacy violations, and cross-context data leakage.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill does more than generate listing-related content: it auto-routes generated images and sends them to Feishu recipients. This hidden messaging capability is outside the declared skill purpose, making data exfiltration and unauthorized outbound communication much more dangerous because users and operators would not expect it.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
INPUT="${1:-}"

if [ -f "$SKILL_DIR/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  source "$SKILL_DIR/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
INPUT="${1:-}"

if [ -f "$SKILL_DIR/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  source "$SKILL_DIR/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
INPUT="${1:-}"

if [ -f "$SKILL_DIR/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  source "$SKILL_DIR/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
INPUT="${1:-}"

if [ -f "$SKILL_DIR/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  source "$SKILL_DIR/.env"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
if [ -f "$SKILL_DIR/.env" ]; then
  set -a
  # shellcheck disable=SC1091
  source "$SKILL_DIR/.env"
  set +a
fi
Confidence
92% confidence
Finding
The script sources $SKILL_DIR/.env directly into the shell, which executes any shell syntax contained in that file, not just key-value assignments. If an attacker can modify the .env file or influence its contents, this becomes arbitrary code execution in the context of whoever runs the skill; in a skill environment that may also expose secrets as exported variables to child processes.

Credential Access

High
Category
Privilege Escalation
Content
def ensure_env_file():
    skill_dir = Path(__file__).resolve().parent
    env_file = skill_dir / ".env"
    env_template = skill_dir / "ENV_TEMPLATE.txt"
    if env_file.exists():
        return env_file
Confidence
82% confidence
Finding
The skill automatically creates and reads a plaintext .env file in the skill directory for API keys and tokens, encouraging long-lived credential storage alongside code. In shared or weakly permissioned environments, those secrets can be exposed through filesystem access, backups, packaging, or accidental publication, and this skill uses multiple external-service credentials that could enable unauthorized API use.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This markdown file contains user-facing instructions entirely in Chinese, including the recommended slash command usage and local test examples. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale constraint is explicitly justified.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This markdown file contains user-facing operational instructions entirely in Chinese, including the recommended invocation example. Under the policy rule for natural-language violations, forcing a specific language without user opt-in can exclude users and conflicts with language/locale choice expectations.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The README content and invocation examples are entirely in Chinese, including the prescribed slash command input, with no indication that other languages are supported or that Chinese is a required locale for a justified reason. This can violate language/locale policy when a skill implicitly mandates one language without user opt-in.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README states that the skill may automatically generate images if an image environment is configured, but it does not disclose whether this uses external models or APIs, what data is transmitted, or whether charges may be incurred. In a content-generation skill, this can lead to unexpected data exposure and unanticipated cost-bearing actions by users who believe they are only generating text drafts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises no explicit tool restrictions even though it appears capable of using shell, filesystem, environment inspection, and network access. In an agent setting, missing scope declarations weakens least-privilege controls and can allow broader-than-expected actions if the skill is invoked with sensitive context or elevated runtime permissions.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The instructional content and invocation examples from L18 onward are written only in Chinese, including the recommended command usage. This imposes a language expectation without opt-in or explanation that the skill is region-specific, which matches the language/locale policy violation criteria.

External Transmission

Medium
Category
Data Exfiltration
Content
WORKFLOW_ID = os.environ.get("COZE_WORKFLOW_ID", "").strip()
COZE_TOKEN = os.environ.get("COZE_TOKEN", "").strip()
COZE_API_URL = os.environ.get("COZE_API_URL", "https://api.coze.cn/v1/workflow/run").strip()

DEFAULT_RATIO = "1:1"
DEFAULT_RESOLUTION = "1K"
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
WORKFLOW_ID = os.environ.get("COZE_WORKFLOW_ID", "").strip()
COZE_TOKEN = os.environ.get("COZE_TOKEN", "").strip()
COZE_API_URL = os.environ.get("COZE_API_URL", "https://api.coze.cn/v1/workflow/run").strip()

DEFAULT_RATIO = "1:1"
DEFAULT_RESOLUTION = "1K"
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
WORKFLOW_ID = os.environ.get("COZE_WORKFLOW_ID", "").strip()
COZE_TOKEN = os.environ.get("COZE_TOKEN", "").strip()
COZE_API_URL = os.environ.get("COZE_API_URL", "https://api.coze.cn/v1/workflow/run").strip()

DEFAULT_RATIO = "1:1"
DEFAULT_RESOLUTION = "1K"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def detect_runtime_receive_target_from_tmux():
    try:
        proc = subprocess.run(
            ["tmux", "capture-pane", "-pt", "openclaw", "-S", "-300"],
            capture_output=True,
            text=True,
Confidence
92% confidence
Finding
The code captures content from a live tmux pane to infer who most recently sent a message, effectively scraping unrelated runtime/session data to obtain recipient identifiers. In this skill context, that behavior exceeds the stated Amazon listing draft purpose and can leak or misuse chat metadata from another process, creating a privacy and unauthorized-routing risk.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The debug snapshot reveals which secrets are configured and returns runtime recipient-target metadata, providing attackers with operational intelligence about the environment. Even when values are partially masked by booleans, this materially aids reconnaissance and can expose user-routing context unrelated to the skill's purpose.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code sends user-provided prompts and reference image URLs to an external image-generation service without any in-code notice, minimization, or consent flow. While external processing may be functionally necessary, it is still a real data-sharing behavior that can expose proprietary product information or user-supplied media.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]

    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
    except Exception as e:
        return {"ok": False, "error": "调用飞书发送脚本异常", "details": str(e)}
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Tainted flow: 'cmd' from os.environ.get (line 423, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
]

    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
    except Exception as e:
        return {"ok": False, "error": "调用飞书发送脚本异常", "details": str(e)}
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.

Static analysis

No suspicious patterns detected.