Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

send image in feishu

v1.0.0

Send images inline in Feishu chat by uploading via API to get image_key, then sending image message using receive_id_type in URL query.

0· 81·0 current·0 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for jamesqin-cn/send-feishu-image.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "send image in feishu" (jamesqin-cn/send-feishu-image) from ClawHub.
Skill page: https://clawhub.ai/jamesqin-cn/send-feishu-image
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Bare skill slug

openclaw skills install send-feishu-image

ClawHub CLI

Package manager switcher

npx clawhub@latest install send-feishu-image
Security Scan
Capability signals
Requires OAuth token
These labels describe what authority the skill may exercise. They are separate from suspicious or malicious moderation verdicts.
VirusTotalVirusTotal
Suspicious
View report →
OpenClawOpenClaw
Suspicious
high confidence
!
Purpose & Capability
The stated purpose (send images via Feishu API) matches the instructions (upload image → get image_key → send). However the skill does not declare that it needs a Node runtime or any credentials, yet the instructions require reading openclaw.json for appId/appSecret and executing node. The need to access local configuration files (openclaw.json) and the presence of a hardcoded appSecret in the SKILL.md are not proportionate to the declared requirements.
!
Instruction Scope
Runtime instructions direct the agent to (a) read openclaw.json for credentials, (b) generate a temp file under /tmp, (c) run that file with node, and (d) read an absolute IMAGE_PATH. Reading a global config file and executing generated code are higher-privilege actions that go beyond a simple message-send helper and are not explicitly constrained or justified in the skill metadata.
Install Mechanism
This is instruction-only (no install spec), which avoids writing code to disk during install. However the runtime relies on an implicit binary (node) that is not declared in required binaries. Generating and executing temporary JS on disk is a runtime install-like action and should have been declared.
!
Credentials
requires.env lists nothing, but the SKILL.md instructs reading openclaw.json to extract appId/appSecret (sensitive). The doc even includes a concrete appSecret value for the 'cto' account. Asking for/using a tenant app secret and reading a global channels config is high-sensitivity and is not represented in the skill's declared requirements — this mismatch is disproportionate and exposes secrets.
Persistence & Privilege
The skill is not always:true and does not request persistent system presence. It instructs creating and removing a temporary file and does not claim to modify other skills or system-wide config. Autonomous invocation is allowed by default but is not combined here with other elevated privilege requests.
What to consider before installing
This skill will create and execute a temporary Node.js script and read your local openclaw.json to get app credentials, yet it declares no required binaries or credentials and even includes a hardcoded appSecret in the instructions. Before installing or using it: (1) Do NOT trust or reuse the hardcoded appSecret — treat it as a leaked secret and rotate it immediately if it is real. (2) Require the author to explicitly declare that node is required and to move credentials to secure env vars or a secrets manager rather than reading global config files. (3) Prefer a version that asks for appId/appSecret at runtime or uses an official SDK instead of writing/executing ad-hoc scripts. (4) If you must run it, run in an isolated environment, audit the generated script before execution, and restrict the Feishu app’s permissions. (5) Ask the publisher why openclaw.json must be read and request they remove the embedded secret from SKILL.md.

Like a lobster shell, security has layers — review code before you run it.

latestvk97818n2ha8qphf51xmzcz1azn84a4hx
81downloads
0stars
1versions
Updated 3w ago
v1.0.0
MIT-0

Feishu Image Send

Send images that render inline in Feishu chat (not as file links).

Problem

The message tool's filePath/file_path parameters often fail for Feishu:

  • API returns ok:true but the recipient sees raw JSON text instead of rendered image
  • Caused by path restrictions (mediaLocalRoots) and outbound handling bugs
  • This skill bypasses the issue by calling the Feishu Open API directly

Workflow

When asked to send an image to a Feishu chat:

  1. Get the image path and target user/chat
  2. Generate a temporary Node.js script with values filled in (see Template below)
  3. Write it to /tmp/feishu-send-{timestamp}.js using write
  4. Run: node /tmp/feishu-send-{timestamp}.js
  5. Confirm ✅ Image sent in output, then clean up: rm /tmp/feishu-send-{timestamp}.js

Script Template

Copy this template and fill in the values:

const https = require('https');
const fs = require('fs');

// === Config: update these values ===
const APP_ID = '<app_id>';             // e.g. cli_a931e5b57ff89cc0
const APP_SECRET = '<app_secret>';     // from openclaw.json
const IMAGE_PATH = '/absolute/path/to/image.jpg';  // must be absolute
const RECEIVE_ID = '<open_id_or_chat_id>';           // e.g. ou_71c53ff7589f8527a27c2a057b96b6d7
const RECEIVE_ID_TYPE = 'open_id';     // or 'chat_id', 'user_id'
// ===================================

function req(url, opts, body) {
  return new Promise((resolve, reject) => {
    const u = new URL(url);
    const r = https.request({
      hostname: u.hostname,
      path: u.pathname + u.search,
      method: opts.method || 'GET',
      headers: opts.headers || {}
    }, res => {
      let d = ''; res.on('data', c => d += c);
      res.on('end', () => { try { resolve(JSON.parse(d)) } catch (e) { resolve(d) } });
    });
    r.on('error', reject);
    if (body) r.write(typeof body === 'string' ? body : JSON.stringify(body));
    r.end();
  });
}

(async () => {
  // 1. Get tenant access token
  const t = await req('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' }
  }, { app_id: APP_ID, app_secret: APP_SECRET });
  if (t.code !== 0) { console.error('Token error:', t); process.exit(1); }
  const token = t.tenant_access_token;
  console.log('Token acquired');

  // 2. Upload image via multipart/form-data
  const boundary = '----Boundary' + Date.now().toString(36);
  const CRLF = '\r\n';
  const img = fs.readFileSync(IMAGE_PATH);
  const fn = IMAGE_PATH.split('/').pop();
  const body = Buffer.concat([
    Buffer.from(`--${boundary}${CRLF}`),
    Buffer.from(`Content-Disposition: form-data; name="image_type"${CRLF}${CRLF}message${CRLF}`),
    Buffer.from(`--${boundary}${CRLF}`),
    Buffer.from(`Content-Disposition: form-data; name="image"; filename="${fn}"${CRLF}`),
    Buffer.from(`Content-Type: image/jpeg${CRLF}${CRLF}`),
    img,
    Buffer.from(`${CRLF}--${boundary}--${CRLF}`),
  ]);
  const uploaded = await new Promise((resolve, reject) => {
    const r = https.request({
      hostname: 'open.feishu.cn',
      path: '/open-apis/im/v1/images',
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': `multipart/form-data; boundary=${boundary}`,
        'Content-Length': body.length
      }
    }, res => {
      let d = ''; res.on('data', c => d += c);
      res.on('end', () => { try { resolve(JSON.parse(d)) } catch (e) { reject(e) } });
    });
    r.on('error', reject); r.write(body); r.end();
  });
  if (uploaded.code !== 0) { console.error('Upload error:', uploaded); process.exit(1); }
  const imageKey = uploaded.data.image_key;
  console.log('Image uploaded, key:', imageKey);

  // 3. Send image message
  // ⚠️ RECEIVE_ID_TYPE must be in URL query string, NOT in JSON body
  const content = JSON.stringify({ image_key: imageKey });
  const sent = await req(
    `https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${encodeURIComponent(RECEIVE_ID_TYPE)}`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json; charset=utf-8'
      }
    },
    { receive_id: RECEIVE_ID, msg_type: 'image', content }
  );
  if (sent.code !== 0) { console.error('Send error:', sent); process.exit(1); }
  console.log('✅ Image sent. message_id:', sent.data.message_id);
})();

Critical Rules

RuleDetail
receive_id_typeMust be a URL query parameter (?receive_id_type=open_id), not in the JSON body
image_type in uploadUse "message" for chat images, not "avatar"
content field in sendMust be a JSON string (double-escaped), not an object
IMAGE_PATHMust be an absolute path to a readable image file

Getting Credentials

Read openclaw.jsonchannels.feishu.accounts.<accountId> for appId and appSecret.

For the CTO agent, these are already known:

  • accountId: cto
  • appId: cli_a931e5b57ff89cc0
  • appSecret: Q3c78ab1ORB7xOj0JtRz2d1GwQtzZcZH

For the target user, use the current conversation's sender open_id from the inbound context.

Supported Formats

JPEG, PNG, GIF, WEBP, TIFF, BMP, ICO — max 30MB.

Common Pitfalls

SymptomCauseFix
99992402 field validation failed for receive_id_typeParameter placed in JSON body instead of URLMove receive_id_type=open_id to the URL query string
234011 Can't recognize image formatCorrupted, missing, or unsupported fileEnsure valid JPEG/PNG and file exists at the given path
Image uploads but not displayed in chatUsed image_type=avatar instead of messageChange image_type to "message" for chat images
message tool returns ok but no image rendersfilePath not in mediaLocalRoots or outbound bugUse this skill's direct API method instead

Integration with Cron Jobs

In automated reports (daily/weekly), generate and run the script programmatically:

const { writeFileSync, unlinkSync } = require('fs');
const { execSync } = require('child_process');
const path = '/tmp/feishu-send-' + Date.now() + '.js';

const script = `/* filled template */`;
writeFileSync(path, script);
execSync(`node ${path}`);
unlinkSync(path);

API Reference

  • Upload Image: POST /open-apis/im/v1/images (multipart/form-data)
  • Send Message: POST /open-apis/im/v1/messages?receive_id_type={type} (JSON body)

Official docs:

Comments

Loading comments...