Back to skill

Security audit

Feishu PDF Uploader

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed Feishu/Lark file uploader, with some implementation and scoping cautions but no evidence of hidden, deceptive, persistent, or unrelated behavior.

Install only if you intend to let the skill upload selected local files to Feishu/Lark using your configured Feishu app credentials. Use explicit file paths and folder tokens, avoid generic upload prompts for sensitive reports, and be aware large files may consume substantial memory or hang if Feishu/network calls stall.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_pdf.py:65
Finding

Unbounded Whole-File Buffering Can Exhaust Memory

Content
View full analysis

Vulnerability Details

File Location: scripts/upload_pdf.py:65-66 and scripts/upload_pdf.py:94-125
Vulnerability Type: Unbounded memory allocation from a caller-selected file
Risk Level: Medium

Vulnerable Code

python
# Read file content
with open(file_path, "rb") as f:
    file_data = f.read()
python
# Build multipart body
body = b""

# upload_id field
body += f"--{boundary}\r\n".encode()
body += b'Content-Disposition: form-data; name="upload_id"\r\n\r\n'
body += upload_id.encode() + b'\r\n'

# seq field (0 for single-part upload)
body += f"--{boundary}\r\n".encode()
body += b'Content-Disposition: form-data; name="seq"\r\n\r\n'
body += b'0\r\n'

# size field
body += f"--{boundary}\r\n".encode()
body += b'Content-Disposition: form-data; name="size"\r\n\r\n'
body += str(file_size).encode() + b'\r\n'

# file field
body += f"--{boundary}\r\n".encode()
body += f'Content-Disposition: form-data; name="file"; filename="{file_name}"\r\n'.encode()
body += b'Content-Type: application/octet-stream\r\n\r\n'
body += file_data
body += b'\r\n'

# End boundary
body += f"--{boundary}--\r\n".encode()

Technical Analysis

The script reads the entire caller-selected file into memory without enforcing a maximum size. It then constructs a second complete multipart request body using repeated concatenation of immutable bytes objects. These operations can temporarily require multiple file-sized allocations.

This implementation does not provide the bounded-memory multipart behavior advertised by the Skill. Although the Feishu API flow uses an upload-part endpoint, the script submits the file as one in-memory part rather than streaming or uploading bounded chunks.

Attack Path

  1. An attacker or untrusted workflow supplies a path to a very large accessible file, including a large sparse file.
  2. The Agent invokes scripts/upload_pdf.py with that path.
  3. `f.re ...[truncated 712 chars]
Remediation
View remediation

Remediation Suggestions

  • Enforce an explicit maximum accepted file size before reading or uploading the file.
  • Implement Feishu multipart uploading with fixed-size chunks and the correct block_num.
  • Read one bounded chunk at a time instead of calling f.read() without a size.
  • Use a streaming multipart implementation where supported rather than assembling the complete request body manually.
  • Avoid repeated immutable byte-string concatenation.
  • Apply process-level memory limits as defense in depth.
  • Reject files whose size exceeds either the configured policy or Feishu API limits before obtaining an upload ID.

T09 · Insecure Skill Coding Practices

Warning
Location
uploader.py:61
Finding

Unused Whole-File Read Enables Memory Exhaustion

Content
View full analysis

Vulnerability Details

File Location: uploader.py:61-62
Vulnerability Type: Unbounded and unnecessary file allocation
Risk Level: Medium

Vulnerable Code

python
# 2. 上传文件内容
with open(file_path, 'rb') as f:
    file_content = f.read()

Technical Analysis

upload_pdf() reads the entire caller-selected file into memory without a size restriction. The resulting file_content value is never used: the implementation proceeds directly to upload_finish and never calls Feishu's upload_part endpoint.

Consequently, this allocation is unnecessary for the implemented network flow and creates an avoidable denial-of-service condition. The incomplete upload sequence also means this implementation is unlikely to upload the file successfully.

Attack Path

  1. An attacker or untrusted task provides a path to a very large file that the Agent can already access.
  2. The Agent invokes FeishuPDFUploader.upload_pdf().
  3. The method obtains upload metadata and executes an unrestricted f.read().
  4. Memory is exhausted or the process is terminated before it reaches upload_finish.

Exploitation requires control or influence over the selected input path and the ability to trigger this method.

Impact Assessment

The issue affects availability rather than authorization. No additional privileges are obtained, but the upload process can be terminated and colocated Agent tasks may experience resource pressure. The affected scope depends on the memory limits and isolation applied to the process.

Remediation
View remediation

Remediation Suggestions

  • Remove the unused whole-file read.
  • Complete the required upload_part operation before calling upload_finish.
  • Upload through bounded chunks or a streaming file object.
  • Validate the file size against an explicit policy and the remote API limit.
  • Calculate block_num from the actual chunking strategy instead of always setting it to one.
  • Add tests that verify both successful content transmission and bounded memory consumption.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_pdf.py:27
Finding

Feishu HTTP Requests Have No Timeouts

Content
View full analysis

Vulnerability Details

File Location: scripts/upload_pdf.py:27, scripts/upload_pdf.py:76, scripts/upload_pdf.py:131, and scripts/upload_pdf.py:143
Vulnerability Type: Indefinitely blocking network operations
Risk Level: Medium

Vulnerable Code

python
resp = requests.post(url, headers=headers, json=data)
python
resp = requests.post(prepare_url, headers=headers, json=prepare_data)
python
resp = requests.post(upload_url, headers=upload_headers, data=body)
python
resp = requests.post(finish_url, headers=headers, json=finish_data)

Technical Analysis

None of the token, prepare, upload, or finish requests specifies a connection or read timeout. Python Requests does not impose a default timeout, so a connection or response that stalls can block the Agent indefinitely.

The destinations are hard-coded official Feishu HTTPS endpoints, and the audit found no unrelated exfiltration destination. Nevertheless, network failure, a malfunctioning proxy, a stalled remote service, or an attacker with relevant network influence can prevent these calls from returning.

Attack Path

  1. The Agent starts an upload and reaches one of the four HTTP requests.
  2. A network intermediary, configured proxy, or remote connection accepts the request but stops making progress.
  3. Because no timeout is configured, the Requests call continues waiting.
  4. The Agent worker remains occupied and the task cannot complete without external cancellation or process termination.

A deliberate attack generally requires control over a relevant network path or proxy. The same impact can occur through ordinary network or service failure.

Impact Assessment

No additional system privileges or Feishu permissions are obtained. The impact is denial of service against the current Agent worker and potentially reduced service capacity if multiple stalled uploads consume multiple workers. The to ...[truncated 77 chars]

Remediation
View remediation

Remediation Suggestions

  • Set explicit connection and read timeouts on every request, for example timeout=(5, 60).
  • Catch requests.Timeout separately and return a controlled failure.
  • Use bounded retries with exponential backoff only where retry semantics are safe.
  • Do not retry a potentially completed upload part without confirming Feishu's idempotency requirements.
  • Apply an overall operation deadline in addition to per-request timeouts.
  • Ensure response parsing and error handling also cover non-JSON and unsuccessful HTTP responses.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The documented behavior claims full file upload support, including multipart/chunked upload, but the implementation reportedly does not actually send file bytes and only performs prepare/finalize-style API calls. This mismatch is security-relevant because users may trust the skill with sensitive files or operational workflows under false assumptions, causing data handling errors, broken audit expectations, or accidental disclosure of metadata/credentials without accomplishing the stated task.

Content

No source excerpt is available for this finding.

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 103)May include surrounding context.

md
def get_tenant_token(app_id: str, app_secret: str) -> str:
    """Get tenant access token from Feishu."""
    url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
    headers = {"Content-Type": "application/json"}
    data = {"app_id": app_id, "app_secret": app_secret}

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

python
def get_tenant_token(app_id: str, app_secret: str) -> str:
    """Get tenant access token from Feishu."""
    url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
    headers = {"Content-Type": "application/json"}
    data = {"app_id": app_id, "app_secret": app_secret}

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises behavior that requires reading local files, using credentials from configuration or environment, and making outbound network requests, but it declares no explicit tool scope or permission boundaries. In an agent environment, this weakens least-privilege controls and can allow the skill to access sensitive local data or secrets more broadly than intended when invoked.

Content

No source excerpt is available for this finding.

Tainted flow: 'url' from requests.post (line 150, 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/upload_pdf.py (reported line 29)May include surrounding context.

python
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
    headers = {"Content-Type": "application/json"}
    data = {"app_id": app_id, "app_secret": app_secret}
    resp = requests.post(url, headers=headers, json=data)
    result = resp.json()
    
    if result.get("code") != 0:

Tainted flow: 'finish_data' from requests.post (line 141, 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/upload_pdf.py (reported line 143)May include surrounding context.

python
finish_url = "https://open.feishu.cn/open-apis/drive/v1/files/upload_finish"
    finish_data = {"upload_id": upload_id, "block_num": 1}
    
    resp = requests.post(finish_url, headers=headers, json=finish_data)
    result = resp.json()
    
    if result.get("code") != 0:

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill silently pulls Feishu credentials from environment variables and ~/.openclaw/openclaw.json, expanding credential access beyond the explicit CLI inputs a user might expect. In an agent-skill context, this is more dangerous because it can use ambient local secrets without clear disclosure, enabling unintended authenticated actions against the user's Feishu tenant.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code reads ~/.openclaw/openclaw.json to obtain Feishu app credentials without prominent user-facing disclosure. In a skill ecosystem, silent access to local secret stores is risky because users may believe they are only uploading a file, not granting the skill access to preexisting account credentials.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrase "上传报告" is broad and can match many ordinary user requests that are not specifically about uploading files to Feishu. In an agent environment, overly broad activation can cause the skill to run in unintended contexts, increasing the chance of unexpected file handling or data transfer to an external service.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The example phrase "保存报告到云上" broadens the apparent activation scope beyond the declared Feishu-specific upload behavior. This can teach routing systems or users that generic cloud-save requests should invoke this skill, potentially causing unintended uploads of sensitive documents to Feishu.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code accesses sensitive credentials via FEISHU_APP_ID and FEISHU_APP_SECRET, then sends them in an HTTP request to obtain a tenant token. Although this is part of authentication, the file provides no user-facing warning, confirmation, or explanatory comment/docstring about using environment-stored secrets and sending them off-host.

Content

No source excerpt is available for this finding.

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

python
"app_secret": self.app_secret
        }
        try:
            resp = requests.post(url, headers=headers, json=data, timeout=10)
            if resp.status_code == 200:
                self.token = resp.json().get("tenant_access_token")
                return self.token

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

python
"app_secret": self.app_secret
        }
        try:
            resp = requests.post(url, headers=headers, json=data, timeout=10)
            if resp.status_code == 200:
                self.token = resp.json().get("tenant_access_token")
                return self.token

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill claims to perform multipart/chunked upload, but it never calls the upload_part step and instead reads the file locally then immediately calls upload_finish. This mismatch is dangerous because users may trust it for reliable large-file transfer or assume data was uploaded when it was not, causing silent failure, data loss, or incorrect workflow automation around sensitive documents.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script uploads local file contents to an external cloud service without any explicit user-facing warning, confirmation, or data-sensitivity notice. In an agent-skill context this is more dangerous because users may invoke it as a convenience tool without realizing it transmits potentially sensitive local documents off-host to a third party.

Content

No source excerpt is available for this finding.

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

python
}
        
        try:
            resp = requests.post(prepare_url, headers=headers, json=prepare_data, timeout=10)
            if resp.status_code != 200:
                return {"error": f"获取上传凭证失败: {resp.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 · uploader.py (reported line 60)May include surrounding context.

python
}
        
        try:
            resp = requests.post(prepare_url, headers=headers, json=prepare_data, timeout=10)
            if resp.status_code != 200:
                return {"error": f"获取上传凭证失败: {resp.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/upload_pdf.py (reported line 143)May include surrounding context.

python
"block_num": 1
            }
            
            resp = requests.post(finish_url, headers=headers, json=finish_data, timeout=10)
            if resp.status_code == 200:
                result = resp.json().get("data", {})
                return {

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

python
"block_num": 1
            }
            
            resp = requests.post(finish_url, headers=headers, json=finish_data, timeout=10)
            if resp.status_code == 200:
                result = resp.json().get("data", {})
                return {

Tainted flow: 'finish_data' from requests.post (line 72, 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 · uploader.py (reported line 77)May include surrounding context.

python
"block_num": 1
            }
            
            resp = requests.post(finish_url, headers=headers, json=finish_data, timeout=10)
            if resp.status_code == 200:
                result = resp.json().get("data", {})
                return {

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The manifest content, aliases, triggers, and examples are entirely in Chinese, with no indication that other languages are supported or that Chinese is an intentional, documented locale restriction. This can violate language/locale policy if users are not offered a choice or informed of the limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Natural-language text exposed to users, including the module description, CLI description, and help strings, is exclusively in Chinese. This imposes a language choice on users without opt-in or alternative locale support, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

Both the manifest and module text emphasize uploading PDF files, but upload_pdf only checks that the path exists and derives the filename; it does not verify extension or content type. This means the code's behavior is broader than the stated PDF-focused purpose.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The function-level documentation specifically states it uploads PDF files, creating an intent expectation for PDF-only handling. The actual code does not check extension, MIME type, or file signature, so the documented intent contradicts the broader behavior.

Content

No source excerpt is available for this finding.