Back to skill

Security audit

Feishu Notes

Security checks for vulnerabilities and agentic risk

Overview

This Feishu document skill is purpose-aligned but needs Review because it requests broad drive access while its promised folder boundary is not enforced and image URLs can be fetched from arbitrary locations.

Review before installing. Only use this with a Feishu app and account where broad drive access is acceptable, avoid granting access to sensitive drives, do not rely on the claimed folder-only boundary, and do not let untrusted prompts or users supply document IDs or image URLs.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/feishu_docs.py:239
Finding

Arbitrary Image URL Fetching Enables SSRF and Internal Data Disclosure

Content
View full analysis

Vulnerability Details

File Location: scripts/feishu_docs.py, lines 239-251
Vulnerability Type: Server-Side Request Forgery (SSRF) with subsequent data upload
Risk Level: High

Vulnerable code:

python
# Step 2: Upload image (download if URL provided)
if image_url and not image_data:
    img_r = requests.get(image_url, timeout=15)
    image_data = img_r.content
if not image_data:
    return False, "No image data"
files = {"file": ("image.png", io.BytesIO(image_data), "image/png")}
data = {"file_name": "image.png", "parent_type": "docx_image", "parent_node": block_id, "size": str(len(image_data))}
r2 = requests.post(f"{BASE}/drive/v1/medias/upload_all",
    headers={"Authorization": f"Bearer {token}"}, data=data, files=files)
result2 = r2.json()

Technical Analysis

The image command accepts a caller-controlled URL and passes it directly to requests.get. The implementation does not validate:

  • The URL scheme
  • The destination hostname
  • Resolved IP addresses
  • Redirect destinations
  • Loopback, private, link-local, or reserved address ranges
  • Cloud metadata endpoints
  • Response MIME type or actual file format
  • Maximum response size

The request library follows redirects by default. Consequently, validating only the initial URL would also be insufficient unless every redirect destination were revalidated.

The complete response is buffered into memory through img_r.content, labeled as image/png without format verification, and uploaded to Feishu. This creates an SSRF primitive that can turn network-reachable internal data into remotely stored content. Unbounded buffering also permits excessive memory consumption.

Attack Path

  1. An attacker influences an Agent request or directly invokes the CLI with an image URL.
  2. The attacker supplies a URL targeting a local service, private network host, link-local endpoint, or a public URL that redirects to such a des ...[truncated 903 chars]
Remediation
View remediation

Remediation Suggestions

  • Permit only https URLs.
  • Use an explicit allowlist of trusted image hosts where operationally possible.
  • Resolve hostnames before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges for both IPv4 and IPv6.
  • Disable redirects, or manually follow them while repeating scheme, hostname, and resolved-IP validation for every destination.
  • Protect against DNS rebinding by ensuring the validated address is the address used for the connection.
  • Stream the response instead of using response.content, and enforce a strict maximum byte count.
  • Set both connection and read timeouts.
  • Validate the response status and reject non-success responses.
  • Check the declared MIME type and verify the actual image format with a safe image parser before upload.
  • Abort and remove the empty image block if download, validation, upload, or binding fails.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/feishu_docs.py:68
Finding

Declared Folder Access Boundary Is Not Enforced

Content
View full analysis

Vulnerability Details

File Location: scripts/feishu_docs.py, lines 68-82
Related locations: scripts/feishu_docs.py, lines 87-219; SKILL.md, documented “Permissions and security rules”
Vulnerability Type: Missing authorization and scope enforcement
Risk Level: Medium

Representative vulnerable code:

python
def list_docs(token, limit=10):
    r = requests.get(f"{BASE}/drive/v1/files?page_size={limit}",
        headers={"Authorization": f"Bearer {token}"})
    data = r.json()
    if data.get("code") != 0:
        return data
    files = data.get("data", {}).get("files", [])
    result = []
    for f in files:
        ft = f.get("token") or f.get("file_token", "")
        ftype = f.get("type", "")
        url_prefix = {"docx": "docx", "sheet": "sheet", "folder": "drive/folder"}.get(ftype, "file")
        result.append({
            "name": f.get("name"),
            "type": ftype,
            "id": ft,
            "url": f"https://bytedance.feishu.cn/{url_prefix}/{ft}" if ft else ""
        })
    return result

Additional affected access patterns:

python
def read_doc(token, doc_id):
    r = requests.get(f"{BASE}/docx/v1/documents/{doc_id}/blocks/{doc_id}",
        headers={"Authorization": f"Bearer {token}"})
python
def write_text(token, doc_id, text):
    """Write plain text paragraph."""
    r = requests.post(f"{BASE}/docx/v1/documents/{doc_id}/blocks/{doc_id}/children",
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
        json={"children": [{"block_type": 2, "text": {"elements": [{"text_run": {"content": text}}]}}]})
    return r.json().get("code") == 0

Technical Analysis

SKILL.md declares that operations are limited to a designated folder and that documents outside that folder must not be viewed, edited, moved, or deleted. The implementation does not enforce this boundar ...[truncated 2303 chars]

Remediation
View remediation

Remediation Suggestions

  • Request the narrowest Feishu scopes that support the required operations instead of full-drive access where narrower scopes are available.
  • Require FEISHU_FOLDER_TOKEN when folder confinement is part of the security model; fail closed if it is absent.
  • Restrict document listings to the configured folder.
  • Before every read, write, heading, bold, code, table, image, move, or future delete operation, resolve the target's parent hierarchy and verify that it is a descendant of the configured folder.
  • Perform authorization checks independently of caller-supplied document IDs.
  • Reject targets whose membership cannot be established, including API errors or ambiguous parent information.
  • Centralize this validation in a shared authorization function so new commands cannot bypass it.
  • Clearly document any residual Feishu platform permissions that cannot technically be narrowed and obtain explicit user consent for them.
  • Add tests proving that documents in sibling folders, the drive root, and unrelated shared locations are rejected.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (48)

Tainted flow: 'APP_ID' from os.environ.get (line 16, 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/feishu_docs.py (reported line 35)May include surrounding context.

python
if token and refresh:
        # Try to refresh proactively - Feishu tokens live ~2h
        r = requests.post(f"{BASE}/authen/v1/refresh_access_token",
            json={
                "app_id": APP_ID,
                "app_secret": APP_SECRET,

Tainted flow: 'APP_ID' from os.environ.get (line 16, 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/feishu_docs.py (reported line 63)May include surrounding context.

python
os.chmod(TOKEN_FILE, 0o600)

def get_app_token():
    r = requests.post(f"{BASE}/auth/v3/tenant_access_token/internal", json={
        "app_id": APP_ID, "app_secret": APP_SECRET
    })
    return r.json().get("tenant_access_token", "")

Tainted flow: 'ACE_FOLDER_TOKEN' from os.environ.get (line 21, 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/feishu_docs.py (reported line 147)May include surrounding context.

python
# Move to Ace folder
    if ACE_FOLDER_TOKEN:
        requests.post(f"{BASE}/drive/v1/files/{doc_id}/move",
            headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
            json={"type": "docx", "folder_token": ACE_FOLDER_TOKEN})

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description understates what the skill can do: beyond basic document CRUD, it can enumerate files, expose token status, insert images from arbitrary external URLs, and perform richer document mutations. This mismatch weakens informed consent and may cause users or orchestrators to approve a skill without understanding its broader data access and exfiltration surface.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

The documented API surface includes destructive delete and move operations even though the agent-facing commands and security guarantees do not clearly constrain or gate them. In a document-management context, exposing parameterized file IDs for destructive operations raises the risk of unauthorized deletion or manipulation of arbitrary documents if scoping and confirmation checks are weak.

Content

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

md
| `GET /docx/v1/documents/:id/blocks/:id` | 读取文档标题 |
| `GET /docx/v1/documents/:id/blocks/:id/children` | 读取文档内容(分页) |
| `POST /docx/v1/documents/:id/blocks/:id/children` | 追加内容块 |
| `DELETE /drive/v1/files/:id?type=docx` | 删除文档 |
| `POST /drive/v1/files/:id/move` | 移动文档到文件夹 |

## 文档格式化规范

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill declares no explicit tool scope even though the documentation clearly implies capabilities involving environment variables, local file writes, network access, and shell execution. Without an allowlist or permissions block, an agent may invoke broader capabilities than users expect, increasing the chance of unintended data access, credential handling, or remote transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill does not prominently warn that user-provided content may be transmitted to Feishu and stored in the user's cloud drive. This omission creates a consent and privacy risk because users may share sensitive notes assuming the action is local or ephemeral.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% 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
echo 'FEISHU_APP_ID=cli_xxxxxxxxxxxxxxxxxxxx' >> ~/.openclaw/.feishu_env
echo 'FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxx' >> ~/.openclaw/.feishu_env
chmod 600 ~/.openclaw/.feishu_env

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

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

bash
echo 'FEISHU_APP_ID=cli_xxxxxxxxxxxxxxxxxxxx' >> ~/.openclaw/.feishu_env
echo 'FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxx' >> ~/.openclaw/.feishu_env
chmod 600 ~/.openclaw/.feishu_env

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

bash
source ~/.openclaw/.feishu_env
cd ~/.openclaw/workspace
python3 scripts/feishu_docs.py create "Hello World"

返回一个飞书链接,点开能看到文档 → 配置成功 🎉

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

bash
source ~/.openclaw/.feishu_env
cd ~/.openclaw/workspace
python3 scripts/feishu_docs.py create "Hello World"

返回一个飞书链接,点开能看到文档 → 配置成功 🎉

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The invocation examples are broad natural-language triggers like '记到飞书' and '读取笔记内容给我看看' without clear boundaries on document selection, data classes, or confirmation requirements. Overly permissive triggers can cause an agent to send sensitive content to Feishu or read cloud documents in response to ambiguous prompts.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

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

Content

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

md
export FEISHU_APP_ID="your_app_id"
export FEISHU_APP_SECRET="your_app_secret"

# 方式二:配置文件(推荐,chmod 600)
echo 'FEISHU_APP_ID=your_app_id' >> ~/.openclaw/.feishu_env
echo 'FEISHU_APP_SECRET=your_app_secret' >> ~/.openclaw/.feishu_env
chmod 600 ~/.openclaw/.feishu_env

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The security rules claim operations are restricted to a designated folder, yet the same document says documents may be created in the cloud root by default and exposes list/read behaviors that are not clearly folder-scoped. This contradiction can mislead users into believing data access is sandboxed when it may not be.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
82% confidence
Finding

Session persistence is a real security consideration here because the script automatically refreshes and continues using cached OAuth credentials for personal-space operations. In an agent-skill context, silent reuse of prior authorization can make unintended document access or modification easier if the environment is shared or compromised.

Content

Scanner excerpt · scripts/feishu_docs.py (reported line 7)May include surrounding context.

python
Usage:
  python3 feishu_docs.py list                    # 列出最近文档
  python3 feishu_docs.py read <doc_id>           # 读取文档内容
  python3 feishu_docs.py create <title>          # 创建新文档(用户身份)
  python3 feishu_docs.py write <doc_id> <text>   # 追加文本段落
  python3 feishu_docs.py token                   # 刷新 user_access_token
  python3 feishu_docs.py code <doc_id> <text>      # 追加代码块

Session Persistence

Medium
Category
Rogue Agent
Confidence
82% confidence
Finding

Session persistence is a real security consideration here because the script automatically refreshes and continues using cached OAuth credentials for personal-space operations. In an agent-skill context, silent reuse of prior authorization can make unintended document access or modification easier if the environment is shared or compromised.

Content

Scanner excerpt · scripts/feishu_docs.py (reported line 7)May include surrounding context.

python
Usage:
  python3 feishu_docs.py list                    # 列出最近文档
  python3 feishu_docs.py read <doc_id>           # 读取文档内容
  python3 feishu_docs.py create <title>          # 创建新文档(用户身份)
  python3 feishu_docs.py write <doc_id> <text>   # 追加文本段落
  python3 feishu_docs.py token                   # 刷新 user_access_token
  python3 feishu_docs.py code <doc_id> <text>      # 追加代码块

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

python
if token and refresh:
        # Try to refresh proactively - Feishu tokens live ~2h
        r = requests.post(f"{BASE}/authen/v1/refresh_access_token",
            json={
                "app_id": APP_ID,
                "app_secret": APP_SECRET,

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

python
os.chmod(TOKEN_FILE, 0o600)

def get_app_token():
    r = requests.post(f"{BASE}/auth/v3/tenant_access_token/internal", json={
        "app_id": APP_ID, "app_secret": APP_SECRET
    })
    return r.json().get("tenant_access_token", "")

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The implementation can enumerate recent files from the user's Feishu drive, which is broader than simple read/write of a specified document and may expose document names, IDs, and links. Understating this capability in the manifest weakens informed consent and can lead operators to grant the skill more trust and access than intended.

Content

No source excerpt is available for this finding.

Tainted flow: 'doc_id' from requests.post (line 143, network input) → requests.get (network output)

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/feishu_docs.py (reported line 89)May include surrounding context.

python
return result

def read_doc(token, doc_id):
    r = requests.get(f"{BASE}/docx/v1/documents/{doc_id}/blocks/{doc_id}",
        headers={"Authorization": f"Bearer {token}"})
    title_data = r.json()
    title = ""

Tainted flow: 'url' from requests.post (line 102, network input) → requests.get (network output)

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/feishu_docs.py (reported line 105)May include surrounding context.

python
url = f"{BASE}/docx/v1/documents/{doc_id}/blocks/{doc_id}/children?page_size=50"
        if page_token:
            url += f"&page_token={page_token}"
        r = requests.get(url, headers={"Authorization": f"Bearer {token}"})
        data = r.json()
        if data.get("code") != 0:
            break

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

python
def create_doc(token, title):
    # Create document
    r = requests.post(f"{BASE}/docx/v1/documents",
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
        json={"title": title})
    data = r.json()

Tainted flow: 'title' from requests.get (line 97, network input) → requests.post (network output)

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/feishu_docs.py (reported line 135)May include surrounding context.

python
def create_doc(token, title):
    # Create document
    r = requests.post(f"{BASE}/docx/v1/documents",
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
        json={"title": title})
    data = r.json()

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

python
# Move to Ace folder
    if ACE_FOLDER_TOKEN:
        requests.post(f"{BASE}/drive/v1/files/{doc_id}/move",
            headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
            json={"type": "docx", "folder_token": ACE_FOLDER_TOKEN})

Tainted flow: 'doc_id' from requests.post (line 143, network input) → requests.post (network output)

Medium
Category
Data Flow
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.

Content

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

python
# Move to Ace folder
    if ACE_FOLDER_TOKEN:
        requests.post(f"{BASE}/drive/v1/files/{doc_id}/move",
            headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
            json={"type": "docx", "folder_token": ACE_FOLDER_TOKEN})

Static analysis

No suspicious patterns detected.