Back to skill

Security audit

飞书语音发送器(TTS) Feishu Voice Sender

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its Feishu voice-message purpose, but it deserves review because it can send original prompts and local audio file contents to third-party services with limited built-in scoping.

Install only if you are comfortable granting Feishu and Volcengine credentials and sending message text, original prompt context, generated audio, and explicitly provided .ogg audio files to those services. Avoid passing sensitive prompt text as user_input, restrict ASR file paths and sizes operationally, confirm recipients before sending, and prefer a pinned requests version plus request timeouts.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (4)

T09 · Insecure Skill Coding Practices

Warning
Location
src/feishu_voice.py:187
Finding

Unnecessary Disclosure of the Complete User Prompt to the TTS Provider

Content
View full analysis

Vulnerability Details

File Location: src/feishu_voice.py:187-190 and src/feishu_voice.py:372
Vulnerability Type: Excessive transmission of potentially sensitive user data
Risk Level: Medium

Vulnerable Code

python
# Add context_texts when context is provided
if context:
    payload["req_params"]["additions"] = json.dumps({
        "context_texts": [context]
    })
python
# Generate voice
opus_path = generate_voice(text, emotion, user_input)

Technical Analysis

send_voice_message() passes the complete user_input value to generate_voice() as its context argument. The function then adds that value to the TTS request as context_texts, causing it to be transmitted to https://openspeech.bytedance.com.

This exceeds the minimum data required to synthesize the requested voice message. The code already calls detect_emotion() locally and derives an emotion label, but the emotion argument accepted by generate_voice() is not used in the outgoing request. Instead, the full original prompt is transmitted.

Although the transmission uses HTTPS and targets the documented TTS provider, user_input may contain secrets, personal information, identifiers, unrelated conversation details, or instructions that are not part of the text the user requested to synthesize. The documentation describes the context as being used for emotion awareness, but it does not adequately communicate that the complete original request is shared with the external provider.

Attack Path

  1. A caller invokes send_voice_message() with legitimate synthesis text.
  2. The caller or surrounding Agent places sensitive or unrelated information in user_input.
  3. send_voice_message() passes the complete value to generate_voice().
  4. generate_voice() serializes it into req_params.additions.context_texts.
  5. The complete prompt is transmitted to the external ByteDance TTS endpoint.
  6. The ...[truncated 635 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not send the raw user_input value to the TTS service by default.
  • Use only the locally derived emotion classification, such as happy, sad, or neutral, if the provider supports an equivalent constrained parameter.
  • If contextual text is indispensable, require explicit user consent before sharing it with the external service.
  • Restrict context to a short, purpose-specific value and apply sensitive-data filtering.
  • Document precisely which fields are transmitted, to which provider, and for what purpose.
  • Remove the unused emotion argument or implement it through a constrained provider-supported option rather than forwarding the complete prompt.

T09 · Insecure Skill Coding Practices

Warning
Location
src/feishu_voice.py:117
Finding

Unbounded Audio File Loading and Base64 Expansion Can Exhaust Memory

Content
View full analysis

Vulnerability Details

File Location: src/feishu_voice.py:117-118 and src/feishu_voice.py:134
Vulnerability Type: Unbounded resource consumption
Risk Level: Medium

Vulnerable Code

python
# Read audio file
with open(audio_path, 'rb') as f:
    audio_data = f.read()
python
"audio": {
    "data": base64.b64encode(audio_data).decode('utf-8'),
    "format": "ogg",
    "codec": "opus",
    "rate": 48000,
    "bits": 16,
    "channel": 1
},

Technical Analysis

recognize_voice_file() checks that the path ends in .ogg, exists, and refers to a file. It does not enforce a maximum file size before reading the entire file into memory.

The file is first materialized as a bytes object. Base64 encoding then creates another representation approximately one-third larger than the original, and UTF-8 decoding plus JSON serialization can create additional in-memory copies. A sufficiently large file can therefore consume several times its on-disk size in process memory.

Checking only the filename extension does not establish that the input is a valid or reasonably sized OGG/Opus audio file. The path may also refer to a very large file or, depending on the surrounding permissions, a symbolic link whose target has an .ogg path.

Attack Path

  1. An attacker or untrusted caller creates or identifies a very large file with an .ogg suffix.
  2. The caller invokes recognize_voice_file() or recognize() with that path.
  3. The Skill reads the complete file into memory with f.read().
  4. The Skill creates additional Base64, string, and JSON representations.
  5. Process memory is exhausted or severe memory pressure stalls or terminates the Agent.
  6. If serialization succeeds, the Skill may also generate an excessively large outbound ASR request.

Impact Assessment

Exploitation does not provide elevated privileges or arbitrary code execution. It can cause denial of se ...[truncated 381 chars]

Remediation
View remediation

Remediation Suggestions

  • Call os.stat() before opening the file and reject files above a conservative documented size limit.
  • Recheck the size while processing where file replacement or concurrent growth is relevant.
  • Validate the actual OGG/Opus container and media properties rather than relying only on the suffix.
  • Consider rejecting symbolic links or resolving the path against an approved input directory when the deployment model permits it.
  • Use bounded streaming transport if supported by the ASR provider.
  • Apply process-level memory, execution-time, and outbound-request-size limits as defense in depth.
  • Return a clear validation error when the file exceeds the accepted limit.

T09 · Insecure Skill Coding Practices

Note
Location
src/feishu_voice.py:307
Finding

Feishu Network Requests Lack Timeouts

Content
View full analysis

Vulnerability Details

File Location: src/feishu_voice.py:307 and src/feishu_voice.py:396
Vulnerability Type: Unbounded network wait and availability risk
Risk Level: Low

Vulnerable Code

python
with open(audio_path, 'rb') as f:
    files = {'file': ('voice.opus', f, 'audio/opus')}
    data = {'file_type': 'opus', 'file_name': 'voice.opus'}
    response = requests.post(url, headers=headers, files=files, data=data)
python
response = requests.post(url, headers=headers, json=payload)

Technical Analysis

The Feishu audio upload and message-send operations call requests.post() without a timeout argument. Unlike other requests in the same file, these operations can wait indefinitely if a connection is established but the server or an intermediary stops making progress.

A missing timeout creates an availability weakness even when the destination is a legitimate HTTPS endpoint. DNS problems, network interception, service degradation, or a stalled response can occupy the executing worker without a defined upper bound.

Attack Path

  1. The Skill begins uploading audio or sending a message to Feishu.
  2. Feishu or a network intermediary accepts the connection but delays or never completes the response.
  3. Because no connect or read timeout is configured, the request remains blocked.
  4. The Skill invocation cannot complete, and an associated Agent worker may remain unavailable.
  5. Repeated stalled invocations can consume multiple workers and amplify the denial-of-service effect.

Impact Assessment

This weakness does not grant access to credentials, files, or elevated privileges. Its impact is limited to availability: the current invocation and potentially the shared Agent worker can remain blocked.

The broader scope depends on the host's concurrency model. In a single-worker deployment, one stalled request may prevent all subsequent work; in a multi-worker deployment, ...[truncated 46 chars]

Remediation
View remediation

Remediation Suggestions

  • Add explicit connect and read timeouts to both Feishu requests, for example timeout=(5, 30).
  • Catch requests.Timeout and return a controlled, non-sensitive error.
  • Use only a small number of bounded retries for transient failures.
  • Apply exponential backoff with jitter and avoid retrying non-idempotent message submissions unless duplicate-send behavior is addressed.
  • Enforce an overall deadline for the complete send workflow.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Dependency Version Is Not Reproducibly Pinned

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1
Vulnerability Type: Unbounded dependency version selection
Risk Level: Low

Vulnerable Code

text
requests>=2.28.0

Technical Analysis

The dependency declaration accepts any installed or future version of requests at or above version 2.28.0. This makes installation results dependent on the package repository state at installation time and prevents the reviewed source tree from identifying the exact code that will execute.

The package name is legitimate, and the audit found no evidence of dependency confusion, typosquatting, or a currently malicious package. The risk arises from the absence of reproducible version constraints and integrity verification rather than from a confirmed malicious dependency.

Attack Path

  1. The Skill is installed at a later time or in a different environment.
  2. Dependency resolution selects a newer release than the one used during review.
  3. The selected release contains an incompatible change, a newly introduced vulnerability, or, in a supply-chain compromise scenario, malicious code.
  4. The package is imported by src/feishu_voice.py.
  5. The dependency executes with the same filesystem, environment, and network privileges as the Skill process.

Impact Assessment

No direct privilege escalation is present in the project code. If a resolved dependency were compromised, it would inherit the Skill process's privileges, including potential access to environment variables containing TTS, ASR, and Feishu credentials.

The current finding establishes non-reproducibility and future supply-chain exposure; it does not establish that the presently available requests package is malicious.

Remediation
View remediation

Remediation Suggestions

  • Pin requests to a reviewed version or a narrowly controlled compatible range.
  • Generate and commit a lock file for reproducible deployments.
  • Require package hashes during installation, such as through a hash-locked requirements file.
  • Use a trusted package index and disable unintended fallback indexes.
  • Automate dependency vulnerability monitoring and update pinned versions through a reviewed process.
  • Record and verify transitive dependency versions in addition to the direct dependency.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (22)

Tainted flow: 'payload' from os.getenv (line 282, 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 · src/feishu_voice.py (reported line 152)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"ASR request failed: {response.status_code} | {response.text[:500]}")

Tainted flow: 'payload' from os.getenv (line 282, 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 · src/feishu_voice.py (reported line 287)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"ASR request failed: {response.status_code} | {response.text[:500]}")

Tainted flow: 'payload' from os.getenv (line 282, 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 · src/feishu_voice.py (reported line 218)May include surrounding context.

python
"context_texts": [context]
        })
    
    response = requests.post(url, headers=headers, json=payload, stream=True, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"TTS request failed: {response.status_code}")

Tainted flow: 'headers' from os.getenv (line 384, 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 · src/feishu_voice.py (reported line 401)May include surrounding context.

python
})
            }
            
            response = requests.post(url, headers=headers, json=payload)
            
            if response.status_code != 200:
                raise RuntimeError(f"Send failed: {response.status_code}")

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 · src/feishu_voice.py (reported line 270)May include surrounding context.

python
def get_feishu_token(config: Dict[str, str]) -> str:
    """获取飞书 access token(带缓存)"""
    import requests
    import time

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 · src/feishu_voice.py (reported line 312)May include surrounding context.

python
def get_feishu_token(config: Dict[str, str]) -> str:
    """获取飞书 access token(带缓存)"""
    import requests
    import time

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares access to sensitive environment variables and clearly relies on network access plus an external binary (ffmpeg), but it does not declare an explicit permission or allowed-tools scope in the skill manifest. That creates a governance gap: reviewers and runtime policy engines cannot easily constrain or audit what the skill is permitted to do, which increases the chance of overbroad execution or unexpected capability use.

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

md
**注意:** 需要系统安装 ffmpeg
- macOS: `brew install ffmpeg`
- Ubuntu: `sudo apt-get install ffmpeg`

## 项目结构

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill can read any local path ending in .ogg and exfiltrate its contents to a third-party ASR service, even though the skill's primary purpose is voice sending rather than unrestricted local file access. In an agent environment, this broad file-read capability can be abused to access sensitive local audio artifacts or other renamed files if an attacker can influence the path argument.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
87% confidence
Finding

This function transmits the contents of a local audio file to an external ASR provider. In the context of an agent skill, external transmission is security-relevant because users or upstream prompts may not expect local file content to be sent off-platform, and the skill accepts arbitrary local .ogg paths.

Content

Scanner excerpt · src/feishu_voice.py (reported line 152)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"ASR request failed: {response.status_code} | {response.text[:500]}")

Tainted flow: 'headers' from requests.post (line 320, 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 · src/feishu_voice.py (reported line 152)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"ASR request failed: {response.status_code} | {response.text[:500]}")

Tainted flow: 'headers' from requests.post (line 320, 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 · src/feishu_voice.py (reported line 287)May include surrounding context.

python
}
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"ASR request failed: {response.status_code} | {response.text[:500]}")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The TTS request fixes the speaker to zh_male_m191_uranus_bigtts, which enforces a Chinese voice/locale in behavior. The file does not provide any user opt-in, language-selection parameter, or documented region-specific justification, so this is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The TTS function sends user-provided text and optional context to a third-party speech API. Because context may contain prior conversation text or sensitive user content, this creates a real data egress path outside the host platform.

Content

Scanner excerpt · src/feishu_voice.py (reported line 218)May include surrounding context.

python
"context_texts": [context]
        })
    
    response = requests.post(url, headers=headers, json=payload, stream=True, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"TTS request failed: {response.status_code}")

Tainted flow: 'headers' from requests.post (line 320, 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 · src/feishu_voice.py (reported line 218)May include surrounding context.

python
"context_texts": [context]
        })
    
    response = requests.post(url, headers=headers, json=payload, stream=True, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"TTS request failed: {response.status_code}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · src/feishu_voice.py (reported line 260)May include surrounding context.

python
opus_path,
            '-y'
        ]
        subprocess.run(cmd, check=True, capture_output=True)
    finally:
        # 清理临时 mp3 文件
        if os.path.exists(mp3_path):

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 · src/feishu_voice.py (reported line 287)May include surrounding context.

python
'app_secret': config['FEISHU_APP_SECRET']
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=30)
    
    if response.status_code != 200:
        raise RuntimeError(f"Failed to get Feishu token: {response.status_code}")

Tainted flow: 'headers' from requests.post (line 320, 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 · src/feishu_voice.py (reported line 325)May include surrounding context.

python
with open(audio_path, 'rb') as f:
        files = {'file': ('voice.opus', f, 'audio/opus')}
        data = {'file_type': 'opus', 'file_name': 'voice.opus'}
        response = requests.post(url, headers=headers, files=files, data=data)
    
    if response.status_code != 200:
        raise RuntimeError(f"Upload failed: {response.status_code}")

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

This call sends an audio message to Feishu, which is the core purpose of the skill but still a real external transmission channel. In an agent setting, any capability that can send content to an external recipient can be abused for data exfiltration if invocation and recipient selection are not tightly governed.

Content

Scanner excerpt · src/feishu_voice.py (reported line 401)May include surrounding context.

python
})
            }
            
            response = requests.post(url, headers=headers, json=payload)
            
            if response.status_code != 200:
                raise RuntimeError(f"Send failed: {response.status_code}")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The skill description and usage instructions are presented in a bilingual but fixed Chinese/English format, with primary operational examples and warnings centered on Chinese phrasing. There is no explicit statement that users may choose their preferred language or locale for interaction, which can conflict with a language-choice policy if user opt-in is required.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is specified as requests>=2.28.0, which allows any future version and does not guarantee a reproducible or reviewed install. This can cause builds to pick up vulnerable or breaking releases unexpectedly, and because requests has had security advisories, leaving the version unpinned increases supply-chain and patch-management risk.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.28.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

The manifest does not pin the requests version, so it is impossible to verify whether the installed package includes fixes for known advisories affecting some requests releases. In a skill that may make network requests to Feishu/Lark or related services, an unverified HTTP client version can expose the environment to known library flaws depending on what gets installed.

Content

No source excerpt is available for this finding.