Back to skill

Security audit

Feishu Assistant

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says: it sends a chosen local image to Feishu using configured Feishu bot credentials, with some disclosure and validation shortcomings to review.

Install only if you intend this skill to use your Feishu bot credentials and upload selected local images to Feishu chats or message threads. Confirm the exact file and destination before sending, and be aware that the local image validation has a RIFF/WebP weakness that could allow a non-image RIFF file to be uploaded if chosen.

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

T09 · Insecure Skill Coding Practices

Note
Location
scripts/send_image.py:41
Finding

Non-Image RIFF Files Bypass Image-Type Validation

Content
View full analysis

Vulnerability Details

File Location: scripts/send_image.py, lines 41–53
Vulnerability Type: Improper file-type validation
Risk Level: Low

Vulnerable Code

python
_ALLOWED_MAGIC = {
    b'\xff\xd8\xff':       'jpeg',
    b'\x89PNG\r\n\x1a\n': 'png',
    b'GIF87a':             'gif',
    b'GIF89a':             'gif',
    b'RIFF':               'webp',  # RIFF....WEBP
    b'BM':                 'bmp',
}

def _validate_image(path: str) -> bool:
    """通过 magic bytes 校验是否为图片,返回 True 表示通过"""
    try:
        with open(path, 'rb') as f:
            header = f.read(12)
        for magic in _ALLOWED_MAGIC:
            if header.startswith(magic):
                return True
        # WebP 特殊判断(RIFF....WEBP)
        if header[:4] == b'RIFF' and header[8:12] == b'WEBP':
            return True
        return False
    except Exception:
        return False

Technical Analysis

The generic b'RIFF' signature is accepted by the loop before the code reaches the format-specific WebP check. RIFF is a container format used by WebP as well as non-image formats such as WAV and AVI. Consequently, any file beginning with a valid-looking RIFF prefix passes the advertised image-only validation even when bytes 8–11 do not contain WEBP.

After validation, the accepted file is opened and transmitted to Feishu by upload_image(). It is always assigned the MIME type image/jpeg, regardless of the detected format. This mismatch further weakens server-side content handling and does not correct the defective local validation.

The network transfer itself is necessary for the Skill’s declared image-sending functionality and uses Feishu’s official HTTPS API. The flaw is that the local restriction intended to limit that transfer to supported image formats is bypassable.

Attack Path

  1. An attacker or user able to control the command-line image path selects a non-image RIFF file, such as a WAV or AVI file.
  2. The script reads its first 12 bytes in `_v ...[truncated 1147 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the generic b'RIFF' entry from _ALLOWED_MAGIC.
  2. Accept a RIFF file as WebP only when both conditions hold:
    • Bytes 0–3 equal RIFF.
    • Bytes 8–11 equal WEBP.
  3. Return the verified format from the validation function instead of only a Boolean result.
  4. Assign the upload MIME type according to that verified format rather than always using image/jpeg.
  5. Consider decoding the file with a maintained image library and rejecting content that cannot be parsed as a supported image.
  6. Enforce a maximum file size before upload to reduce accidental disclosure and resource-consumption risks.
  7. Reject ambiguous or malformed files rather than accepting them based on a short shared container signature.

A minimal correction would be:

python
_ALLOWED_MAGIC = {
    b'\xff\xd8\xff':       'jpeg',
    b'\x89PNG\r\n\x1a\n': 'png',
    b'GIF87a':             'gif',
    b'GIF89a':             'gif',
    b'BM':                 'bmp',
}

def _validate_image(path: str) -> bool:
    try:
        with open(path, 'rb') as f:
            header = f.read(12)

        if header[:4] == b'RIFF':
            return header[8:12] == b'WEBP'

        return any(header.startswith(magic) for magic in _ALLOWED_MAGIC)
    except (OSError, PermissionError):
        return False
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tainted flow: 'app_id' from os.environ.get (line 72, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/send_image.py (reported line 96)May include surrounding context.

python
def get_tenant_token(app_id, app_secret):
    """获取 tenant_access_token"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=10

Tainted flow: 'token' from os.environ.get (line 201, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/send_image.py (reported line 110)May include surrounding context.

python
def upload_image(token, image_path):
    """上传图片到飞书,获取 image_key"""
    with open(image_path, "rb") as f:
        resp = requests.post(
            "https://open.feishu.cn/open-apis/im/v1/images",
            headers={"Authorization": f"Bearer {token}"},
            files={

Tainted flow: 'token' from os.environ.get (line 201, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/send_image.py (reported line 127)May include surrounding context.

python
def send_image_by_id(token, receive_id_type, receive_id, image_key):
    """通过 receive_id 发送图片"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/im/v1/messages",
        params={"receive_id_type": receive_id_type},
        headers={"Authorization": f"Bearer {token}"},

Tainted flow: 'token' from os.environ.get (line 201, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/send_image.py (reported line 147)May include surrounding context.

python
def send_image_reply(token, message_id, image_key):
    """通过 message_id 回复图片"""
    url = f"https://open.feishu.cn/open-apis/im/v1/messages/{message_id}/reply"
    resp = requests.post(
        url,
        headers={"Authorization": f"Bearer {token}"},
        json={

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill documentation describes capabilities that require environment access and outbound network communication, but the manifest does not declare any tool scope or permission boundaries. This weakens least-privilege controls and makes it easier for an agent to invoke broader capabilities than a reviewer or user would expect, especially since the skill also auto-loads credentials from local config and environment variables.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill description omits a clear warning that images will be uploaded to an external service, which can mislead users about data handling and destination. For a skill that sends user-provided images over the network using stored credentials, lack of disclosure increases the chance of unintended sharing of sensitive or private content.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Broad trigger phrases like '发送到飞书' or '发到群里' can match normal conversation and cause the skill to activate unexpectedly, leading to accidental transmission of images to an external platform. In the context of a messaging skill with network access and credential auto-loading, ambiguous invocation materially raises the risk of unintentional data exfiltration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and all user-facing CLI descriptions are written only in Chinese, which imposes a specific language on users without any opt-in or alternative locale. Under the policy, language constraints should either be optional for the user or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

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.py (reported line 96)May include surrounding context.

python
def get_tenant_token(app_id, app_secret):
    """获取 tenant_access_token"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=10

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.py (reported line 110)May include surrounding context.

python
def get_tenant_token(app_id, app_secret):
    """获取 tenant_access_token"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=10

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.py (reported line 127)May include surrounding context.

python
def get_tenant_token(app_id, app_secret):
    """获取 tenant_access_token"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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.py (reported line 96)May include surrounding context.

python
def get_tenant_token(app_id, app_secret):
    """获取 tenant_access_token"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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.py (reported line 127)May include surrounding context.

python
def send_image_by_id(token, receive_id_type, receive_id, image_key):
    """通过 receive_id 发送图片"""
    resp = requests.post(
        "https://open.feishu.cn/open-apis/im/v1/messages",
        params={"receive_id_type": receive_id_type},
        headers={"Authorization": f"Bearer {token}"},

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The reply URL is built by directly interpolating message_id into the path without validation. While the host is fixed to Feishu, malformed or attacker-controlled message_id values could cause unintended API path manipulation, request failure patterns, or misuse of the bot to post into unintended conversation contexts if upstream controls are weak.

Content

Scanner excerpt · scripts/send_image.py (reported line 147)May include surrounding context.

python
def send_image_reply(token, message_id, image_key):
    """通过 message_id 回复图片"""
    url = f"https://open.feishu.cn/open-apis/im/v1/messages/{message_id}/reply"
    resp = requests.post(
        url,
        headers={"Authorization": f"Bearer {token}"},
        json={

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill claims zero-configuration credential loading from both environment variables and a local OpenClaw config file, expanding its access beyond simple image sending into credential discovery and local secret use. This increases the risk of unintended secret consumption and silent use of privileged accounts, particularly because the user may not realize the skill will search local configuration and transmit data externally.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The manifest description at L03 limits the skill to sending images to Feishu via user_id, open_id, or chat_id. The documented usage additionally supports sending based on a message_id, which is a behavior not reflected in the manifest's stated interface and scope.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.