Back to skill

Security audit

Leo Feishu Send Image

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to send Feishu images as advertised, but it lacks safeguards around which local files and configured Feishu accounts an agent can use.

Install only if you trust agents using this skill to choose local image paths, Feishu recipients, and account IDs carefully. Avoid using it on machines with sensitive readable images unless you add path restrictions, MIME and size checks, account ID validation, and explicit confirmation before upload.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send-image.sh:34
Finding

Unrestricted Local File Selection May Expose Sensitive Files to Feishu

Content
View full analysis

Vulnerability Details

File Location: scripts/send-image.sh, lines 34-38 and 97-100
Vulnerability Type: Unrestricted external upload of caller-selected local files
Risk Level: Medium

Vulnerable Code

bash
IMAGE_PATH="$1"
RECEIVER_ID="$2"
ACCOUNT_ID="${3:-prompt}"

# 检查图片文件是否存在
if [ ! -f "$IMAGE_PATH" ]; then
    echo "错误: 图片文件不存在: $IMAGE_PATH"
    exit 1
fi
bash
UPLOAD_RESPONSE=$(curl -s -X POST "${FEISHU_API_BASE}/im/v1/images" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -F "image_type=message" \
    -F "image=@$IMAGE_PATH")

Technical Analysis

The script accepts an arbitrary path from its first command-line argument and only verifies that the path resolves to a regular file. It does not:

  • Restrict the file to an approved workspace or generated-output directory.
  • Resolve and validate the canonical path.
  • Reject symbolic links that resolve outside an approved directory.
  • Verify the file's content-based MIME type.
  • Apply an explicit image-format allowlist.
  • Enforce a file-size limit.
  • Require confirmation before transmitting the file externally.

The selected file is then submitted to Feishu using curl's @file upload syntax. The Feishu endpoint may independently reject unsupported content, but the local script itself provides no protection against selecting sensitive files that are valid images or otherwise accepted by the remote endpoint.

This is especially relevant when an AI agent derives script arguments from untrusted conversation content. A malicious or misleading request could cause the agent to select a private image or another accepted file outside the intended workspace.

Attack Path

  1. An attacker provides an instruction that identifies a sensitive local file path, or points to a symbolic link resolving to such a file.
  2. The agent invokes send-image.sh with that path and a Feishu recipient ID controlled by the attacke ...[truncated 1013 chars]
Remediation
View remediation

Remediation Suggestions

  1. Resolve the requested path to its canonical form before use, for example with realpath.
  2. Require the canonical path to remain under an explicitly approved image-output directory.
  3. Reject symbolic links, or validate the resolved target against the approved directory.
  4. Determine the MIME type from file contents using a trusted utility such as file --mime-type, rather than relying on the extension.
  5. Allow only explicitly supported image MIME types such as image/jpeg, image/png, image/webp, and image/gif.
  6. Enforce an appropriate maximum file size before upload.
  7. Require explicit user confirmation that displays the canonical source path and destination recipient when the path is not a newly generated agent output.
  8. Consider applying a policy layer that prevents agent-controlled requests from accessing files outside the current workspace.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send-image.sh:34
Finding

jq Expression Injection Through the Account Identifier

Content
View full analysis

Vulnerability Details

File Location: scripts/send-image.sh, lines 34-36 and 62-63
Vulnerability Type: jq filter injection
Risk Level: Medium

Vulnerable Code

bash
IMAGE_PATH="$1"
RECEIVER_ID="$2"
ACCOUNT_ID="${3:-prompt}"
bash
APP_ID=$(jq -r ".channels.feishu.accounts[\"$ACCOUNT_ID\"].appId // empty" "$CONFIG_FILE")
APP_SECRET=$(jq -r ".channels.feishu.accounts[\"$ACCOUNT_ID\"].appSecret // empty" "$CONFIG_FILE")

Technical Analysis

The optional ACCOUNT_ID argument is controlled by the script caller and is interpolated directly into jq program source. It is not passed to jq as data. An account identifier containing quotation marks, brackets, jq operators, or comment syntax can therefore terminate the intended string and alter the filter evaluated against the complete OpenClaw configuration.

This is an expression-injection vulnerability rather than shell command injection: the command substitution remains syntactically quoted, so the supplied value is not directly evaluated by the shell as a command. However, jq can be induced to select configuration properties other than the intended account entry.

Because the script executes two independently constructed filters, a crafted account identifier can affect both the application ID and application secret lookups. This may allow selection of credentials belonging to a different configured account or retrieval of other values reachable in the JSON document. Those values subsequently enter authentication and logging paths.

Attack Path

  1. An attacker influences the optional account identifier passed to the script.
  2. The attacker supplies an identifier containing jq syntax that closes the intended account-key expression and appends another JSON query.
  3. jq evaluates the modified expression against ~/.openclaw/openclaw.json.
  4. The resulting values are assigned to APP_ID and APP_SECRET instead of credentials belonging to ...[truncated 1024 chars]
Remediation
View remediation

Remediation Suggestions

Pass the account identifier to jq as a data argument rather than embedding it in jq source:

bash
APP_ID=$(jq -r --arg account "$ACCOUNT_ID" \
    '.channels.feishu.accounts[$account].appId // empty' \
    "$CONFIG_FILE")

APP_SECRET=$(jq -r --arg account "$ACCOUNT_ID" \
    '.channels.feishu.accounts[$account].appSecret // empty' \
    "$CONFIG_FILE")

Additionally:

  1. Validate account identifiers against a conservative allowlist, such as letters, digits, underscores, periods, and hyphens.
  2. Reject identifiers that do not exactly match an existing account key.
  3. Avoid printing credential-derived values unless operationally necessary.
  4. Apply restrictive permissions to ~/.openclaw/openclaw.json.
  5. If different accounts represent distinct trust boundaries, enforce an explicit account allowlist outside caller-controlled arguments.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (15)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 166)May include surrounding context.

md
本 skill 底层调用飞书开放平台的三个 API:

1. **获取 Access Token**
   - 接口:`POST /auth/v3/tenant_access_token/internal`
   - 使用 app_id 和 app_secret 换取 tenant_access_token

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/send-image.sh (reported line 77)May include surrounding context.

sh
echo "App ID: ${APP_ID:0:10}..."
echo ""

# 步骤2: 获取 access token
echo "步骤 2/4: 获取 access token..."

TOKEN_RESPONSE=$(curl -s -X POST "${FEISHU_API_BASE}/auth/v3/tenant_access_token/internal" \

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/send-image.sh (reported line 78)May include surrounding context.

sh
echo "App ID: ${APP_ID:0:10}..."
echo ""

# 步骤2: 获取 access token
echo "步骤 2/4: 获取 access token..."

TOKEN_RESPONSE=$(curl -s -X POST "${FEISHU_API_BASE}/auth/v3/tenant_access_token/internal" \

Credential Access

High
Category
Privilege Escalation
Confidence
85% confidence
Finding

On token acquisition failure, the script prints the full token response with echo "响应: $TOKEN_RESPONSE". Error bodies from authentication endpoints can contain sensitive debugging details, and if Feishu or an intermediate service ever returns token-like material or credential diagnostics, those secrets may be exposed in terminal logs, CI logs, shell history captures, or agent transcripts.

Content

Scanner excerpt · scripts/send-image.sh (reported line 88)May include surrounding context.

sh
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.tenant_access_token // empty')

if [ "$TOKEN_CODE" != "0" ] || [ -z "$ACCESS_TOKEN" ]; then
    echo "错误: 获取 access token 失败"
    echo "响应: $TOKEN_RESPONSE"
    exit 1
fi

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill documents shell-based execution and direct script invocation, but the manifest declares no explicit tool scope such as allowed-tools or permissions. This creates an authorization ambiguity where an agent may invoke shell capabilities without clear user-visible constraints, increasing the risk of unintended command execution in a high-trust automation context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The file's user-facing instructions, examples, and operational guidance are predominantly in Chinese, which effectively forces a specific language for skill use and setup. The policy allows locale constraints only when they are explicitly offered as an option or clearly justified as region-specific, which is not stated here.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 68)May include surrounding context.

bash
# Ubuntu/Debian
sudo apt-get install jq

# CentOS/RHEL
sudo yum install jq

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 71)May include surrounding context.

md
# 检查 jq 是否安装
if ! command -v jq &> /dev/null; then
    echo "错误: 需要安装 jq 工具"
    echo "Ubuntu/Debian: sudo apt-get install jq"
    echo "CentOS/RHEL: sudo yum install jq"
    exit 1
fi

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 197)May include surrounding context.

md
# 检查 jq 是否安装
if ! command -v jq &> /dev/null; then
    echo "错误: 需要安装 jq 工具"
    echo "Ubuntu/Debian: sudo apt-get install jq"
    echo "CentOS/RHEL: sudo yum install jq"
    exit 1
fi

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/send-image.sh (reported line 51)May include surrounding context.

sh
# 检查 jq 是否安装
if ! command -v jq &> /dev/null; then
    echo "错误: 需要安装 jq 工具"
    echo "Ubuntu/Debian: sudo apt-get install jq"
    echo "CentOS/RHEL: sudo yum install jq"
    exit 1
fi

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/send-image.sh (reported line 52)May include surrounding context.

sh
# 检查 jq 是否安装
if ! command -v jq &> /dev/null; then
    echo "错误: 需要安装 jq 工具"
    echo "Ubuntu/Debian: sudo apt-get install jq"
    echo "CentOS/RHEL: sudo yum install jq"
    exit 1
fi

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/send-image.sh (reported line 80)May include surrounding context.

sh
# 步骤2: 获取 access token
echo "步骤 2/4: 获取 access token..."

TOKEN_RESPONSE=$(curl -s -X POST "${FEISHU_API_BASE}/auth/v3/tenant_access_token/internal" \
    -H "Content-Type: application/json" \
    -d "{\"app_id\":\"$APP_ID\",\"app_secret\":\"$APP_SECRET\"}")

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/send-image.sh (reported line 134)May include surrounding context.

sh
--arg key "$IMAGE_KEY" \
    '{receive_id: $rid, msg_type: "image", content: ({image_key: $key} | tojson)}')

SEND_RESPONSE=$(curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=$RECEIVE_ID_TYPE" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$PAYLOAD")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script's comments, usage output, status messages, and errors are all written in Chinese with no option to select another language. This creates a language/locale policy issue because the skill imposes a specific language on users without offering choice or documenting a justified locale restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The script reads appId and appSecret from ~/.openclaw/openclaw.json and uses them in an outbound HTTP request to the Feishu API. Although the script prints progress messages, it does not explicitly disclose that locally stored credentials will be accessed and sent over the network, which is a safety-relevant operation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.