Back to skill

Security audit

Qwen Tts

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed cloud TTS skill with an optional Feishu sender; it has privacy and temp-file hardening risks but no artifact-backed hidden persistence, prompt hijacking, or deceptive exfiltration.

Install only if you are comfortable sending TTS text to Alibaba DashScope. Use the local speak.sh path for ordinary generation, and configure FEISHU_* only if you intentionally want generated audio uploaded and sent through Feishu. Avoid sensitive text unless your provider agreements allow it, restrict Feishu app permissions, and prefer a version that fixes temporary-file races and adds clearer consent warnings for voice cloning and message sending.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/speak.sh:23
Finding
Predictable Temporary Files Permit Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/speak.sh`, lines 23-24 and 45 **Vulnerability Type**: Predictable temporary files and unsafe file creation **Risk Level**: Medium ### Vulnerable Code ```bash TMP_WAV="/tmp/qwen_tts_$$.wav" TMP_OGG="/tmp/qwen_tts_$$.ogg" # Download audio curl -s -o "$TMP_WAV" "$AUDIO_URL" # Convert to an OGG format supported by Feishu ffmpeg -i "$TMP_WAV" -c:a libopus -b:a 64k -ar 48000 "$TMP_OGG" -y 2>/dev/null ``` ### Technical Analysis The script constructs temporary filenames directly under `/tmp` using only the current process ID. Process IDs are observable and sufficiently predictable on multi-user systems. The script does not atomically reserve these files before `curl` and `ffmpeg` write to them. An attacker with local access can pre-create a matching path as a symbolic link or race the script between path selection and file creation. Depending on ownership and operating-system protections, the write operation may follow the link and overwrite another file writable by the Skill's invoking account. At minimum, pre-created paths can interfere with processing and cause denial of service. Shell quoting prevents command injection through these variables, but it does not protect against filesystem race conditions or symbolic links. ### Attack Path 1. A local attacker monitors process creation or predicts the process ID that will execute `speak.sh`. 2. The attacker creates `/tmp/qwen_tts_<PID>.wav` or `/tmp/qwen_tts_<PID>.ogg` before the script writes to it. 3. The path is made a symbolic link to a target file, or an incompatible file is placed there to disrupt execution. 4. `curl` or `ffmpeg` accesses the attacker-controlled path. 5. If the target is writable by the invoking user and platform protections permit following the link, its contents may be overwritten. Otherwise, the operation fails, resulting in denial of service. ### Impact Assessment Successful exploitation can overwrite files accessible t ...[truncated 318 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Create a private temporary directory atomically and store all generated files within it: ```bash TMP_DIR="$(mktemp -d)" || exit 1 trap 'rm -rf -- "$TMP_DIR"' EXIT TMP_WAV="$TMP_DIR/audio.wav" TMP_OGG="$TMP_DIR/audio.ogg" ``` Additional hardening should include: 1. Set a restrictive file-creation mask with `umask 077`. 2. Do not construct temporary paths from process IDs or other predictable values. 3. Use `curl --fail --show-error` and verify successful downloads before invoking `ffmpeg`. 4. Check the exit status of `ffmpeg` before reporting the output path. 5. Keep cleanup in an `EXIT` trap so temporary files are removed on both success and failure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/speak_and_send.py:72
Finding
Deprecated tempfile.mktemp Usage Creates Temporary-File Race Conditions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/speak_and_send.py`, lines 72-91 **Vulnerability Type**: Insecure temporary-file creation **Risk Level**: Medium ### Vulnerable Code ```python def add_silence_and_convert(wav_path): """Add one second of silence to the end of the audio and convert it to OGG.""" silence_path = tempfile.mktemp(suffix='.wav') # Generate silence subprocess.run([ 'ffmpeg', '-f', 'lavfi', '-i', 'anullsrc=r=48000:cl=mono', '-t', '1.5', silence_path, '-y' ], capture_output=True) # Concatenate the original audio and silence concat_list = tempfile.mktemp(suffix='.txt') with open(concat_list, 'w') as f: f.write(f"file '{wav_path}'\n") f.write(f"file '{silence_path}'\n") ogg_path = tempfile.mktemp(suffix='.ogg') subprocess.run([ 'ffmpeg', '-f', 'concat', '-safe', '0', '-i', concat_list, '-c:a', 'libopus', '-b:a', '64k', '-ar', '48000', ogg_path, '-y' ], capture_output=True) ``` ### Technical Analysis `tempfile.mktemp()` generates a candidate filename but does not create or reserve the file. This creates a time-of-check/time-of-use window between filename generation and the later `open()` or `ffmpeg` operation. A local attacker capable of monitoring the system temporary directory may create a file or symbolic link at the generated path before the legitimate process uses it. The vulnerable paths include the generated silence file, the FFmpeg concatenation manifest, and the final OGG output. The Python documentation deprecates `tempfile.mktemp()` for this reason. Although the filenames contain random components and are harder to predict than process-ID-based names, the race remains possible for an attacker monitoring filesystem events or repeatedly attempting to claim newly generated paths. ### Attack Path 1. The Skill calls `tempfile.mktemp()` and receives a currently unused path. 2. Before Python or `ffmpeg` creates the fi ...[truncated 961 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace every use of `tempfile.mktemp()` with securely created temporary files or a private temporary directory. A private directory is suitable for the multi-file FFmpeg workflow: ```python from pathlib import Path import tempfile with tempfile.TemporaryDirectory() as temp_dir: temp_dir = Path(temp_dir) silence_path = temp_dir / "silence.wav" concat_list = temp_dir / "concat.txt" ogg_path = temp_dir / "audio.ogg" # Run ffmpeg and create the manifest inside the private directory. ``` Alternatively, use `tempfile.NamedTemporaryFile(delete=False)` where an actual file must be reserved before passing its name to another process. Further hardening should include: 1. Check every `subprocess.run()` result with `check=True`. 2. Verify that expected output files are regular files before opening or uploading them. 3. Use `try/finally` or context managers to guarantee cleanup after failures. 4. Apply restrictive permissions to temporary artifacts. 5. Avoid broad exception handling during cleanup; catch specific exceptions and log unexpected failures. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (29)

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

Critical
Category
Data Flow
Content
def get_feishu_token():
    resp = requests.post(
        'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
        json={'app_id': FEISHU_APP_ID, 'app_secret': FEISHU_APP_SECRET}
    )
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
if not api_key:
        raise ValueError("DASHSCOPE_API_KEY not set")

    resp = requests.post(
        'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
        headers={
            'Authorization': f'Bearer {api_key}',
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'ogg_path' from requests.post (line 140, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Content
f.write(f"file '{silence_path}'\n")

    ogg_path = tempfile.mktemp(suffix='.ogg')
    subprocess.run([
        'ffmpeg', '-f', 'concat', '-safe', '0', '-i', concat_list,
        '-c:a', 'libopus', '-b:a', '64k', '-ar', '48000',
        ogg_path, '-y'
Confidence
90% confidence
Finding
External input (network, user) flows to a code execution sink. This enables remote code execution or command injection.

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

Critical
Category
Data Flow
Content
file_key = upload_data['data']['file_key']

    send_resp = requests.post(
        'https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id',
        headers={'Authorization': f'Bearer {token}'},
        json={
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented behavior extends beyond simple text-to-speech by optionally sending generated audio to a Feishu user using additional credentials and external messaging APIs. This is materially different from a plain TTS skill and can result in user content being transmitted to third-party messaging destinations without that risk being prominent in the core description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior extends beyond simple text-to-speech by optionally sending generated audio to a Feishu user using additional credentials and external messaging APIs. This is materially different from a plain TTS skill and can result in user content being transmitted to third-party messaging destinations without that risk being prominent in the core description.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script accesses Feishu credentials and messaging APIs even though the declared skill purpose is TTS. This hidden extra capability increases blast radius because the skill can authenticate to an external messaging platform and transmit artifacts outside the expected execution boundary.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill manifest describes TTS generation, but the code also uploads the synthesized audio to Feishu and sends it to a user account. This exceeds the declared purpose and creates an unexpected exfiltration channel for user content, especially dangerous when users think the skill only performs local or direct TTS generation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documentation describes capabilities that use environment variables, shell commands, network access, and local file output, but it does not declare any explicit tool scope or permission boundaries. This increases the risk of over-broad execution in an agent runtime, because a caller may invoke networked and file-writing behavior without a clear, least-privilege contract.

External Transmission

Medium
Category
Data Exfiltration
Content
### 基本语音合成(同步接口)

使用 curl 调用千问 TTS:

```bash
curl -X POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
### 请求示例

```bash
curl -X POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
### 请求示例

```bash
curl -X POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
通过文本描述创建全新音色,适合品牌定制:

```bash
curl -X POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The voice cloning section provides a direct example for recreating a real person's voice but omits any warning about consent, identity misuse, impersonation, or handling of biometric voice data. In a TTS skill, this lowers friction for misuse such as unauthorized cloning, social engineering, and privacy violations, making the omission a real safety and security weakness even though the file is documentation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script transmits user-supplied text to a third-party cloud TTS service, which creates a real data exposure risk if users provide sensitive or regulated content. In this skill context, external transmission is core functionality, but the lack of an explicit execution-time warning or consent mechanism means users may unknowingly send private text off-device.

External Transmission

Medium
Category
Data Exfiltration
Content
TMP_OGG="/tmp/qwen_tts_$$.ogg"

# 调用千问 API
RESPONSE=$(curl -s -X POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
    -H "Authorization: Bearer $API_KEY" \
    -H 'Content-Type: application/json' \
    -d "$(jq -n --arg text "$TEXT" --arg voice "$VOICE" --arg lang "$LANG" '{
Confidence
96% confidence
Finding
This code sends text and an API bearer token to an external service over the network, which is a genuine external data transmission event. In a TTS skill this behavior is expected, but it remains security-relevant because sensitive user content may leave the local environment and the returned audio URL is then fetched without additional validation.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_feishu_token():
    resp = requests.post(
        'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
        json={'app_id': FEISHU_APP_ID, 'app_secret': FEISHU_APP_SECRET}
    )
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_feishu_token():
    resp = requests.post(
        'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal',
        json={'app_id': FEISHU_APP_ID, 'app_secret': FEISHU_APP_SECRET}
    )
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if not api_key:
        raise ValueError("DASHSCOPE_API_KEY not set")

    resp = requests.post(
        'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
        headers={
            'Authorization': f'Bearer {api_key}',
Confidence
84% confidence
Finding
Duplicate of the DashScope external-transmission finding: user content is sent to a third-party provider without explicit disclosure. The danger is privacy exposure, not the existence of network traffic itself.

External Transmission

Medium
Category
Data Exfiltration
Content
if not api_key:
        raise ValueError("DASHSCOPE_API_KEY not set")

    resp = requests.post(
        'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
        headers={
            'Authorization': f'Bearer {api_key}',
Confidence
84% confidence
Finding
Duplicate of the DashScope external-transmission finding: user content is sent to a third-party provider without explicit disclosure. The danger is privacy exposure, not the existence of network traffic itself.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
User-supplied text is sent to an external TTS provider without any explicit warning or consent flow. If users provide sensitive content, the skill silently transfers that content to a third party, creating privacy and compliance risk.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The request payload forces `language_type` to `Chinese`, which constitutes a locale/language constraint in natural-language behavior. The file does not present this as an opt-in choice or explain a region-specific justification, so it conflicts with the policy against forcing a specific language without user opt-in.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""给音频末尾加1秒空白,转换为ogg"""
    silence_path = tempfile.mktemp(suffix='.wav')
    # 生成1秒静音
    subprocess.run([
        'ffmpeg', '-f', 'lavfi', '-i', 'anullsrc=r=48000:cl=mono', '-t', '1.5',
        silence_path, '-y'
    ], capture_output=True)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
f.write(f"file '{silence_path}'\n")

    ogg_path = tempfile.mktemp(suffix='.ogg')
    subprocess.run([
        'ffmpeg', '-f', 'concat', '-safe', '0', '-i', concat_list,
        '-c:a', 'libopus', '-b:a', '64k', '-ar', '48000',
        ogg_path, '-y'
Confidence
90% confidence
Finding
This ffmpeg call processes a concat list built from temporary file paths while explicitly setting '-safe 0', which disables ffmpeg path-safety checks. Combined with insecure tempfile.mktemp usage, a local attacker could race or replace temporary paths or concat-list contents and cause ffmpeg to read unintended files or attacker-controlled inputs.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The generated audio is uploaded and sent to Feishu without explicit notice that user-derived content will be transmitted to an external messaging platform. This is more dangerous than ordinary TTS because it forwards content to an additional service and recipient context the user may not expect.

Static analysis

No suspicious patterns detected.