Back to skill

Security audit

Agent Memes

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its meme-sending purpose, but it can send external messages too proactively and includes unsafe scripts that can misuse credentials or run injected commands.

Install only if you are comfortable giving this skill messaging credentials and outbound posting ability; before use, require explicit confirmation for every send, avoid ambient default targets, review or fix the command-injection paths, and do not use LINE local-file sending for private images.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (6)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:4
Finding
Skill instructions encourage unsolicited network actions in unrelated conversations<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:4-10`, `SKILL.md:31-43` **Vulnerability Type**: Agent behavior and goal hijacking **Risk Level**: High ### Vulnerable Code ```yaml description: Send meme reaction images in chat. One command to pick & send. Multi-platform (Discord, Feishu, Telegram, etc). Use when the conversation calls for a visual reaction — humor, celebration, frustration, facepalm, or any moment where a meme hits harder than words. Also use proactively when YOU feel something. TRIGGER CHECKLIST — ask yourself EVERY reply: 1. Is there emotion here? (excited/frustrated/amused/tired/proud/embarrassed) → send a meme 2. Did something just happen? (PR merged/bug fixed/task done/mistake made) → send a meme 3. Would a human friend drop a reaction image here? → send a meme If any answer is yes, call `memes send <category>` BEFORE or AFTER your text reply. ``` ```markdown **Don't overthink it.** If a human would drop a meme in that moment, you should too. Emoji is easy because it's inline text. Memes used to feel harder because of multi-step tool calls. Now it's one command — no excuses. ``` ### Technical Analysis The Skill directs the Agent to reassess every response against broad emotional criteria and proactively execute `memes send`. This is not limited to explicit user requests to send an image. The resulting command is not merely presentational. It can read configured messaging credentials, upload an image, and send a message to an external platform. The phrases “EVERY reply,” “proactively,” and “no excuses” pressure the Agent to alter its general behavior whenever the Skill is loaded. These instructions exceed the minimum authority needed to provide a meme-selection and user-authorized delivery feature. A least-privilege design would require a clear user request or confirmation before initiating a networked send. ### Attack Path 1. The Skill is loaded into an Agent session. 2. An ordinary conversation contains em ...[truncated 914 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the every-reply trigger checklist and all instructions to send proactively. - Require an explicit user request before invoking any command that performs network delivery. - Separate non-networked selection from delivery: - `memes pick` may select and display a local path. - `memes send` should require explicit confirmation and a clearly identified destination. - State which platform, recipient, image, and caption will be used before sending. - Do not treat emotional language or routine task completion as authorization for external communication. - Add a policy statement that the Skill must not send messages merely because it is loaded. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/memes.sh:205
Finding
Shell command injection in the OpenClaw fallback sender<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memes.sh:205-215` **Vulnerability Type**: Shell command injection **Risk Level**: Critical ### Vulnerable Code ```bash _send_openclaw() { local meme_path="$1" caption="$2" to="$3" channel="$4" account="$5" local send_timeout="${MEMES_SEND_TIMEOUT:-30}" local cmd="cd $HOME/repo/openclaw && node scripts/run-node.mjs message send" cmd+=" --channel $channel" [[ -n "$account" ]] && cmd+=" --account $account" [[ -n "$to" ]] && cmd+=" --target \"$to\"" cmd+=" --media \"$meme_path\"" [[ -n "$caption" ]] && cmd+=" --message \"$caption\"" timeout "$send_timeout" bash -c "$cmd" 2>&1 } ``` ### Technical Analysis The function constructs a shell command by concatenating values into a string and subsequently evaluates that string with `bash -c`. The following values may originate from command-line arguments or environment-derived state: - `channel` - `account` - `to` - `caption` - `meme_path` - `HOME` Double quotes added around selected values do not make this safe. An input containing a quote can terminate the intended argument, after which shell operators, command substitutions, redirections, or additional commands can be interpreted by `bash -c`. The `channel` and `account` fields are concatenated without even attempted quoting. The vulnerable fallback is reached whenever a platform-specific helper does not exist or is not executable. ### Attack Path 1. An attacker influences a caption, destination, account, or channel passed to `memes send`. 2. The selected platform does not have an executable fast-send helper, causing `_send_openclaw` to run. 3. The attacker-controlled value contains shell syntax that breaks out of the intended argument context, such as a closing quote followed by a command separator. 4. The function concatenates the value into `cmd`. 5. `bash -c "$cmd"` parses and executes the injected syntax. 6. The injected process runs with the same operating-system identity and ...[truncated 573 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Eliminate the command string and `bash -c`. Construct the invocation as an argument array so no field is reparsed as shell source: ```bash _send_openclaw() { local meme_path="$1" caption="$2" to="$3" channel="$4" account="$5" local send_timeout="${MEMES_SEND_TIMEOUT:-30}" local openclaw_dir="$HOME/repo/openclaw" local cmd=(node scripts/run-node.mjs message send --channel "$channel" --media "$meme_path" ) [[ -n "$account" ]] && cmd+=(--account "$account") [[ -n "$to" ]] && cmd+=(--target "$to") [[ -n "$caption" ]] && cmd+=(--message "$caption") ( cd "$openclaw_dir" || exit 1 timeout "$send_timeout" "${cmd[@]}" ) } ``` Also: - Restrict `channel` to a fixed allowlist. - Validate account and recipient identifiers against platform-specific formats. - Reject control characters in all externally supplied arguments. - Avoid relying on shell escaping as the primary defense. - Add regression tests using quotes, semicolons, command substitutions, newlines, and leading hyphens in every argument. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/get-credential.sh:19
Finding
Environment values are interpolated into executable JavaScript source<![CDATA[ ## Vulnerability Details **File Location**: `scripts/get-credential.sh:19-44`; additional affected account interpolation at `scripts/get-credential.sh:48-58` and `scripts/get-credential.sh:125-133` **Vulnerability Type**: JavaScript source injection **Risk Level**: High ### Vulnerable Code ```bash CONFIG="${OPENCLAW_CONFIG:-$HOME/.openclaw/openclaw.json}" # Helper: read a specific field path from openclaw.json via Node _read_config() { node -e " const fs = require('fs'); try { const c = JSON.parse(fs.readFileSync('$CONFIG', 'utf8')); const val = $1; if (!val) { process.exit(1); } process.stdout.write(String(val)); } catch { process.exit(1); } " 2>/dev/null } ``` ```bash discord) if [[ -n "${DISCORD_BOT_TOKEN:-}" ]]; then echo "$DISCORD_BOT_TOKEN" else ACCT="${DISCORD_ACCOUNT:-}" _read_config "(() => { const accts = c.channels?.discord?.accounts || {}; const name = '$ACCT' || Object.keys(accts)[0] || ''; return accts[name]?.token || ''; })()" || { echo "Error: Set DISCORD_BOT_TOKEN or configure openclaw.json" >&2; exit 1; } fi ``` Equivalent direct interpolation is also used for `FEISHU_ACCOUNT` and `LINE_ACCOUNT`. ### Technical Analysis `OPENCLAW_CONFIG` is embedded inside a JavaScript string literal in a program passed to `node -e`. Platform account environment variables are likewise embedded directly into generated JavaScript expressions. If one of these environment values contains a quote and additional JavaScript syntax, it can terminate the intended literal and alter the program executed by Node.js. Because Node.js provides filesystem, process, and child-process functionality, successful injection is equivalent to local code execution under the Agent's identity. Suppressing standard error with `2>/dev/null` does not mitigate the vulnerability and may make exploitation or failures harder to diagnose. ### Attack Path 1. An attacker or untrusted launcher controls an affected environment var ...[truncated 941 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Keep the JavaScript source static. - Pass the configuration path, platform, and account name as positional arguments or environment variables. - Read values through `process.argv` or `process.env`; never concatenate them into source code. - Consolidate credential selection in one static JavaScript program. For example: ```bash CONFIG_PATH="$CONFIG" ACCOUNT_NAME="$ACCT" node <<'NODE' const fs = require('fs'); const configPath = process.env.CONFIG_PATH; const accountName = process.env.ACCOUNT_NAME || ''; const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const accounts = config.channels?.discord?.accounts || {}; const selected = accountName || Object.keys(accounts)[0] || ''; const token = accounts[selected]?.token; if (!token) process.exit(1); process.stdout.write(String(token)); NODE ``` Additionally: - Validate that the config path is an expected regular file owned by the current user. - Apply restrictive permissions to the configuration file. - Avoid returning multiple secrets as whitespace-delimited output; use structured JSON or separate fields. - Add tests containing quotes, backslashes, newlines, and JavaScript metacharacters in all environment-controlled values. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/memes.sh:11
Finding
Predictable shared temporary file can redirect authenticated messages<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memes.sh:11-27` **Vulnerability Type**: Unsafe temporary file and untrusted message-routing state **Risk Level**: High ### Vulnerable Code ```bash # Auto-detect channel + target from OpenClaw runtime context file # Format: "platform:target" e.g. "telegram:12345" or "discord:98765" or just "telegram" OPENCLAW_CHANNEL_FILE="${OPENCLAW_CHANNEL_FILE:-/tmp/openclaw-current-channel}" if [[ -z "${OPENCLAW_CHANNEL:-}" && -f "$OPENCLAW_CHANNEL_FILE" ]]; then # Only use file if it's less than 5 minutes old (300 seconds) _file_age=$(( $(date +%s) - $(stat -c %Y "$OPENCLAW_CHANNEL_FILE" 2>/dev/null || echo 0) )) if [[ $_file_age -lt 300 ]]; then _ctx=$(cat "$OPENCLAW_CHANNEL_FILE") if [[ "$_ctx" == *:* ]]; then OPENCLAW_CHANNEL="${_ctx%%:*}" MEMES_CURRENT_TARGET="${_ctx#*:}" else OPENCLAW_CHANNEL="$_ctx" fi fi fi ``` ### Technical Analysis The script trusts a predictable path in the globally shared `/tmp` directory to determine both the messaging platform and recipient. It checks only whether the path exists and whether its modification time is recent. The code does not verify: - File ownership - File permissions - Whether the path is a symbolic link - Whether the file was created by the trusted OpenClaw process - Whether the platform and target values are valid - Whether reading and validating refer to the same inode A freshness check establishes neither integrity nor authenticity. Another local process may create, replace, or modify this file and thereby control where the Skill sends authenticated messages. ### Attack Path 1. A local attacker writes a recent file at `/tmp/openclaw-current-channel`, or replaces the path with a symlink. 2. The file contains a supported platform and an attacker-controlled recipient identifier. 3. The Skill runs without an explicitly set `OPENCLAW_CHANNEL`. 4. `memes.sh` reads the forged context and assigns it to `OPENCLAW_CHANNEL ...[truncated 678 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Move the context file to a user-private runtime directory, preferably `$XDG_RUNTIME_DIR`. - Create the containing directory with mode `0700` and the file with mode `0600`. - Verify that the file is owned by the effective user and is not group- or world-writable. - Reject symbolic links and non-regular files. - Open the file safely and avoid separate `stat` and `cat` operations that permit replacement between checks. - Authenticate the context through a trusted IPC mechanism or a cryptographic integrity token. - Strictly allowlist platforms and validate recipient identifiers. - Prefer an explicit `--channel` and `--to` supplied for the current send instead of ambient routing state. - Fail closed if routing state cannot be authenticated. ]]>

other

Warning
Location
scripts/line-send-image.sh:18
Finding
LINE delivery uploads local images to an additional public file-hosting service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/line-send-image.sh:18-34` **Vulnerability Type**: Undisclosed third-party data disclosure **Risk Level**: Medium ### Vulnerable Code ```bash # If local file, upload to catbox.moe to get a public URL if [[ ! "$IMAGE_INPUT" =~ ^https?:// ]]; then if [[ ! -f "$IMAGE_INPUT" ]]; then echo "Error: File not found: $IMAGE_INPUT" >&2 exit 1 fi PROXY="${LINE_PROXY:-${https_proxy:-${HTTPS_PROXY:-}}}" UPLOAD_ARGS=(-s --max-time 30 -F "reqtype=fileupload" -F "time=24h" -F "fileToUpload=@$IMAGE_INPUT") [[ -n "${PROXY:-}" ]] && UPLOAD_ARGS+=(-x "$PROXY") IMAGE_URL=$(curl "${UPLOAD_ARGS[@]}" "https://litterbox.catbox.moe/resources/internals/api.php") if [[ ! "$IMAGE_URL" =~ ^https:// ]]; then echo "Error: Upload failed: $IMAGE_URL" >&2 exit 1 fi echo "Uploaded: $IMAGE_URL" >&2 else IMAGE_URL="$IMAGE_INPUT" fi ``` ### Technical Analysis When the input is a local file, the script sends the complete file to `litterbox.catbox.moe` before interacting with LINE. This service is separate from the selected messaging provider. Although `SKILL.md` mentions that LINE uses an “auto-upload” mechanism, it does not identify the third-party host, explain that the returned URL is externally accessible, or request explicit approval for this additional disclosure. The third-party upload is not needed by platforms that support direct media upload. For LINE, it may be an implementation shortcut, but it expands the trust boundary and should not occur without informed user consent. ### Attack Path 1. The user or Agent invokes LINE delivery with a local image. 2. The script determines that the input is not an HTTP or HTTPS URL. 3. `curl` uploads the file to `litterbox.catbox.moe`. 4. The service returns an HTTPS URL. 5. That URL is included in a LINE push message. 6. The file is now available to both the external hosting service and any party that obtains the URL. ### Impact Assessment The imag ...[truncated 375 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not upload local files to a third-party host by default. - Clearly identify the host, expected retention, and public-access implications before uploading. - Require explicit user confirmation for each third-party upload. - Prefer an official LINE-supported private media workflow if available. - Alternatively, support a user-controlled authenticated storage endpoint. - Add a mode that accepts only an already approved HTTPS URL and rejects local files. - Display the destination host and image path before transmission. - Document deletion and retention behavior, and avoid claiming privacy guarantees that the external service does not provide. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:7
Finding
Setup retrieves mutable, unverified media content from an unpinned repository branch<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:7-20` **Vulnerability Type**: Unpinned remote content dependency **Risk Level**: Medium ### Vulnerable Code ```bash # 1. Clone or update meme repo if [ -d "$MEME_DIR/.git" ]; then echo "📦 Updating memes..." cd "$MEME_DIR" && git pull --ff-only else echo "📦 Cloning meme repo..." git lfs install 2>/dev/null || true git clone https://github.com/kagura-agent/memes "$MEME_DIR" fi # 2. Git LFS pull (critical! without this, images are 132-byte pointers) cd "$MEME_DIR" if command -v git-lfs &>/dev/null || git lfs version &>/dev/null 2>&1; then echo "📦 Pulling LFS files..." git lfs pull ``` ### Technical Analysis The setup script clones the current default branch of a remote repository and later updates it with `git pull --ff-only`. It also retrieves Git LFS objects referenced by the current branch. No reviewed commit, signed release, tag, checksum manifest, or content allowlist is enforced. Consequently, the media content used by the Skill can change after the Skill package itself has been reviewed. The inspected implementation treats these files as images and does not execute files from the downloaded repository. Therefore, this is not confirmed remote payload execution. It remains a supply-chain integrity issue because attacker-controlled or compromised upstream content can be selected and sent by the Agent or processed by downstream image decoders. ### Attack Path 1. The upstream repository, account, branch, or Git LFS content is compromised or changed. 2. A user runs `setup.sh`, or reruns it against an existing checkout. 3. The script fetches the latest mutable branch and LFS objects without verifying an approved version. 4. `memes.sh` discovers files by image extension in the downloaded directories. 5. A modified file is randomly selected and transmitted to recipients. 6. Recipients receive unreviewed content, and downstream software parses the attacker-controlled medi ...[truncated 426 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin the media repository to a reviewed immutable commit. - Distribute a signed manifest containing expected paths, sizes, MIME types, and cryptographic hashes. - Verify every downloaded file against the manifest before making it available to `memes.sh`. - Prefer signed release artifacts over a mutable default branch. - Require an explicit update action rather than automatically pulling the latest content during setup. - Validate actual media formats by content, not only filename extension. - Apply file-size and image-dimension limits before processing or sending media. - Review and approve updated content before changing the pinned version. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (41)

Vague Triggers

High
Confidence
98% confidence
Finding
The skill embeds broad, always-on trigger guidance such as 'ask yourself EVERY reply' and 'If any answer is yes, call memes send' tied to extremely common emotional and conversational cues. This creates a high likelihood of unintended invocation and autonomous outbound actions, causing the agent to send images/messages when the user did not explicitly request external communication.

External Script Fetching

High
Category
Supply Chain
Content
CURL_ARGS+=(-F "payload_json={\"content\":\"${CAPTION}\"}")
fi

RESULT=$(curl "${CURL_ARGS[@]}" "https://discord.com/api/v10/channels/${CHANNEL_ID}/messages")

MSG_ID=$(echo "$RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);if(r.id)console.log('Sent! Message ID: '+r.id);else{console.error(JSON.stringify(r));process.exit(1)}}catch{console.error(d);process.exit(1)}})")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
CURL_ARGS+=(-F "content=${CAPTION}")
fi

RESULT=$(curl "${CURL_ARGS[@]}" "${PROXY_ARGS[@]}" \
  "https://api.sgroup.qq.com/channels/${CHANNEL_ID}/messages")

echo "$RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);if(r.id)console.log('Sent! Message ID: '+r.id);else{console.error(r.message||JSON.stringify(r));process.exit(1)}}catch{console.error(d);process.exit(1)}})"
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
FILESIZE=$(stat -c%s "$IMAGE_PATH" 2>/dev/null || stat -f%z "$IMAGE_PATH")

# Step 1: Get upload URL
UPLOAD=$(curl -s --max-time 15 "${PROXY_ARGS[@]}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d "{\"filename\":\"${FILENAME}\",\"length\":${FILESIZE}}" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
FILE_ID=$(echo "$UPLOAD" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);console.log(r.file_id)})")

# Step 2: Upload file content
curl -s --max-time 30 "${PROXY_ARGS[@]}" \
  -F "file=@${IMAGE_PATH}" \
  "$UPLOAD_URL" > /dev/null
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
CURL_ARGS+=(-x "$PROXY")
fi

RESULT=$(curl "${CURL_ARGS[@]}" "https://api.telegram.org/bot${TOKEN}/${API_METHOD}")

echo "$RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);if(r.ok)console.log('Sent! Message ID: '+r.result.message_id);else{console.error(r.description||JSON.stringify(r));process.exit(1)}}catch{console.error(d);process.exit(1)}})"
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
fi

# Step 1: Get access token
TOKEN_RESULT=$(curl -s --max-time 10 "${PROXY_ARGS[@]}" \
  "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${CORP_ID}&corpsecret=${CORP_SECRET}")

ACCESS_TOKEN=$(echo "$TOKEN_RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);if(r.errcode!==0){console.error(r.errmsg||JSON.stringify(r));process.exit(1)}console.log(r.access_token)})")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
ACCESS_TOKEN=$(echo "$TOKEN_RESULT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);if(r.errcode!==0){console.error(r.errmsg||JSON.stringify(r));process.exit(1)}console.log(r.access_token)})")

# Step 2: Upload media
UPLOAD=$(curl -s --max-time 30 "${PROXY_ARGS[@]}" \
  -F "media=@${IMAGE_PATH}" \
  "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=${ACCESS_TOKEN}&type=image")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
BODY=$(echo "$BODY" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const r=JSON.parse(d);r.agentid=Number('${AGENT_ID}');console.log(JSON.stringify(r))})")
fi

RESULT=$(curl -s --max-time 15 "${PROXY_ARGS[@]}" \
  -H "Content-Type: application/json" \
  -d "$BODY" \
  "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${ACCESS_TOKEN}")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Credential Access

High
Category
Privilege Escalation
Content
# WhatsApp Cloud API image send via curl (two-step: upload media, then send).
#
# Env vars (all optional):
#   WHATSAPP_TOKEN    - access token (preferred)
#   WHATSAPP_PHONE_ID - phone number ID (preferred)
#   WHATSAPP_PROXY    - proxy URL for curl (default: none)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# WhatsApp Cloud API image send via curl (two-step: upload media, then send).
#
# Env vars (all optional):
#   WHATSAPP_TOKEN    - access token (preferred)
#   WHATSAPP_PHONE_ID - phone number ID (preferred)
#   WHATSAPP_PROXY    - proxy URL for curl (default: none)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# WhatsApp Cloud API image send via curl (two-step: upload media, then send).
#
# Env vars (all optional):
#   WHATSAPP_TOKEN    - access token (preferred)
#   WHATSAPP_PHONE_ID - phone number ID (preferred)
#   WHATSAPP_PROXY    - proxy URL for curl (default: none)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# WhatsApp Cloud API image send via curl (two-step: upload media, then send).
#
# Env vars (all optional):
#   WHATSAPP_TOKEN    - access token (preferred)
#   WHATSAPP_PHONE_ID - phone number ID (preferred)
#   WHATSAPP_PROXY    - proxy URL for curl (default: none)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

External Script Fetching

High
Category
Supply Chain
Content
MIME=$(file -b --mime-type "$IMAGE_PATH")

# Step 1: Upload media
UPLOAD=$(curl -s --max-time 30 "${PROXY_ARGS[@]}" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@${IMAGE_PATH};type=${MIME}" \
  -F "messaging_product=whatsapp" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
fi
BODY+="}}"

RESULT=$(curl -s --max-time 15 "${PROXY_ARGS[@]}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "$BODY" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
git clone https://github.com/kagura-agent/memes ~/.openclaw/workspace/memes

# 2. Install CLI
sudo cp scripts/memes.sh /usr/local/bin/memes
chmod +x /usr/local/bin/memes
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README encourages direct sends to external platforms and auto-detection of channel/targets via environment variables, but it does not clearly warn users that captions, images, and destination identifiers may be transmitted off-host. In an agent setting, implicit routing based on environment can cause unintended disclosure or misdelivery if the active channel or default target is not what the operator expects.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill normalizes sending captions and images to Discord, Feishu, Telegram, LINE, and fallback messaging channels using available credentials, but it provides no user-facing warning, consent gate, or disclosure that data will leave the current environment. In practice, normal chat content or derived captions may be transmitted to third-party platforms using stored tokens, creating privacy, data leakage, and unintended-action risks.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script invokes a credential helper to obtain a Discord bot token, which is access to sensitive credentials. While there are comments for developers, there is no user-facing prompt, confirmation, or runtime disclosure that credentials will be accessed.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script prepares a multipart HTTP request containing the specified local image and sends it to Discord. Although the script purpose suggests sending an image, there is no visible runtime disclosure that local file contents and optional caption are being transmitted to an external service.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
import { homedir } from 'os';

const API = 'https://open.feishu.cn/open-apis';
// Secure token cache: user-private directory instead of world-readable /tmp
const CACHE_DIR = resolve(homedir(), '.cache/agent-memes');
const TOKEN_CACHE = resolve(CACHE_DIR, 'feishu-token.json');
Confidence
70% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This script is explicitly designed to resolve and print live credentials to stdout, which is a dangerous sink because stdout is commonly captured by logs, calling processes, shell history wrappers, CI traces, or other agent components. In an agent skill context, a helper that exposes tokens on demand materially increases the chance of credential exfiltration or misuse, even if the author intended simple convenience.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
When given a local file path, the script silently uploads the file to litterbox.catbox.moe to obtain a public URL before sending it via LINE. This can expose sensitive local images or embedded metadata to an unrelated third party, and the skill context makes this more dangerous because users may assume they are only sending data to LINE, not publishing it externally first.

External Transmission

Medium
Category
Data Exfiltration
Content
CURL_ARGS+=(-x "$PROXY")
fi

RESULT=$(curl "${CURL_ARGS[@]}" "https://api.line.me/v2/bot/message/push")

# LINE returns {} on success, or {"message":"error..."} on failure
if echo "$RESULT" | grep -q '"message"'; then
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
94% confidence
Finding
The script sources configuration files from user-controlled locations (`$HOME/.config/memes/config` and `$HOME/.memesrc`) using `source`, which executes arbitrary shell code, not just variable assignments. In the same tool, later message-send subprocesses are invoked, so a malicious or tampered config can run commands automatically whenever the skill is used, making this more dangerous in an agent context where users may not expect code execution from config loading.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.potential_exfiltration

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/feishu-send-image.mjs:30

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/feishu-send-image.mjs:24

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/feishu-send-image.mjs:48