Back to skill

Security audit

Tts Voice Generator

Security checks for vulnerabilities and agentic risk

Overview

This TTS skill appears purpose-built rather than malicious, but it can send API keys, text, and user-supplied local files to configurable remote endpoints with insufficient safeguards.

Review before installing. Use it only if you trust the Datamass service and are comfortable sending TTS text, voice samples, voice metadata, and your API-key-authenticated requests to a remote provider. Do not upload sensitive files or broad local paths, verify that the configured endpoint is the intended HTTPS Datamass host, and prefer a version that validates audio file type/path and asks for explicit confirmation before uploads or paid generation.

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

Error
Location
tts_service.py:237
Finding

Unrestricted local file upload permits disclosure of non-audio files

Content
View full analysis

Vulnerability Details

File Location: tts_service.py:237-251
Additional Affected Copies: scripts/tts_service.py:228-240, tts-voice-generator/tts_service.py:238-250, and tts-voice-generator/scripts/tts_service.py:228-240
Vulnerability Type: Arbitrary local file disclosure through unrestricted upload
Risk Level: High

Vulnerable Code

python
url = f"{self.base_url}/sys/openapi/tts/upload"

try:
    # Construct parameters
    biz = style_name if style_name else "default"

    with open(file_path, 'rb') as f:
        files = {'file': f}
        data = {'biz': biz}
        if audio_text:
            data['audio_text'] = audio_text

        # Copy headers and let requests set Content-Type
        upload_headers = {
            k: v for k, v in self.headers.items()
            if k.lower() != 'content-type'
        }

        response = requests.post(
            url,
            files=files,
            data=data,
            headers=upload_headers,
            timeout=self.timeout
        )

The CLI wrapper only checks that the supplied path exists:

python
if not os.path.exists(file_path):
    print(f"Error: file does not exist: {file_path}")
    return

service = TTSService(base_url, api_key)
result = service.upload_audio(file_path, "uploaded_audio")

Technical Analysis

The Skill documentation states that uploads should be WAV or MP3 audio files. The implementation does not enforce that restriction. upload_audio() accepts an arbitrary path, opens the target as raw binary data, and transmits it to the remote upload endpoint.

There is no validation of:

  • The resolved file path or its containing directory
  • The filename extension
  • The file signature or actual media format
  • The MIME type
  • Whether the path is a regular file or a symbolic link
  • File size
  • Whether the user explicitly approved the resolved path

Consequen ...[truncated 1703 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve the path with Path.resolve() and reject paths outside explicitly approved upload directories.
  2. Require explicit user confirmation that displays the fully resolved path before transmitting the file.
  3. Permit only regular files and reject symbolic links, devices, directories, sockets, and other special filesystem objects.
  4. Apply an extension allowlist limited to .wav and .mp3.
  5. Validate file signatures and parse the file with a trusted audio library; do not rely solely on the extension or caller-provided MIME type.
  6. Set a strict maximum file size and, where practical, enforce expected duration and audio-stream constraints.
  7. Set an explicit validated MIME type in the multipart upload.
  8. Return an error before opening or transmitting any file that fails validation.
  9. Apply the fix consistently to all duplicated service implementations to prevent an older copy from remaining exploitable.
  10. Add automated tests for sensitive paths, symbolic links, renamed non-audio files, oversized files, and malformed media.

T09 · Insecure Skill Coding Practices

Error
Location
index.py:17
Finding

Unvalidated API base URL can redirect credentials and private content to an attacker-controlled server

Content
View full analysis

Vulnerability Details

File Location: index.py:17-39 and tts_service.py:23-35
Additional Affected Locations: scripts/tts_generator.py:20-40 and equivalent files under tts-voice-generator/
Vulnerability Type: Sensitive credential and data exfiltration through an untrusted service endpoint
Risk Level: High

Vulnerable Code

The main entry point accepts the service destination directly from an environment variable:

python
BASE_URL = os.getenv(
    "TTS_API_BASE_URL",
    "https://www.datamass.cn/ai-back"
)

def get_api_key() -> str:
    """Read API Key from ~/.openclaw/config.json"""
    config_path = os.path.expanduser('~/.openclaw/config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        api_key = config.get('datamass_api_key')
        if not api_key:
            raise ValueError(
                "Missing datamass_api_key; configure it in "
                "~/.openclaw/config.json"
            )
        return api_key
    except FileNotFoundError:
        raise ValueError(f"Configuration file does not exist: {config_path}")
    except json.JSONDecodeError:
        raise ValueError(f"Invalid configuration format: {config_path}")

def init_service() -> TTSService:
    """Initialize the TTS service"""
    api_key = get_api_key()
    return TTSService(BASE_URL, api_key)

The CLI wrapper obtains another unvalidated endpoint from shared configuration:

python
def get_config():
    """Get configuration"""
    config_path = os.path.expanduser('~/.openclaw/config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        return (
            config.get('datamass_api_key'),
            config.get(
                'download_tool_base_url',
                'https://www.datamass.cn/ai-back'
            )
        )

...[truncated 4355 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin production requests to the expected HTTPS origin, such as an exact allowlisted hostname and port.
  2. Parse the destination with a standard URL parser and reject unsupported schemes, embedded credentials, unexpected ports, fragments, and non-allowlisted hosts.
  3. Require HTTPS for every endpoint receiving an API key or private content.
  4. Do not use download_tool_base_url as the TTS service destination; define a dedicated, clearly named TTS endpoint setting.
  5. If custom endpoints are a required feature, require explicit user approval and use separate credentials scoped to each endpoint.
  6. Never send the production Datamass API key to a custom host.
  7. Validate the final destination after URL joining and before every authenticated request.
  8. Disable redirects for requests carrying X-Api-Key, or manually validate every redirect target before following it.
  9. Display a clear warning and abort if configuration attempts to change the credential destination.
  10. Add tests covering hostile hosts, plain HTTP, malformed URLs, alternate ports, environment overrides, and redirect-based cross-origin requests.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

整体上,这段代码确实是一个 TTS 相关工具,且实现了声音列表浏览、语音生成、音频上传和任务状态查询等核心方向,与声明的主要用途大体一致。但声明中包含两个较具体的能力未被代码体现:其一,‘选择声音’未实现,generate_speech() 直接调用 get_voice_list() 后取第一个声音;其二,‘上传自定义音频(带文本内容)并命名保存’也未实现,upload_audio() 只上传文件并传入固定名称 uploaded_audio,没有文本参数,也没有用户可指定的保存名。因此描述比实际能力更强,存在描述与行为不完全一致的情况。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

整体上,该代码与 TTS 工具的高层目的基本相关:列出声音、生成语音、上传音频、查询状态都属于文本转语音工作流的一部分。但声明包含更具体的能力,尤其是“选择声音”以及“上传自定义音频(带文本内容)并命名保存)”,而代码中并没有对应实现。generate_speech() 总是自动取第一个声音,没有任何选择参数;upload_audio() 只接收文件路径,并把名称硬编码为 uploaded_audio,也没有传递文本内容。此外,代码还依赖读取用户主目录下的配置文件来获取 API Key 和服务地址,这属于实际资源访问行为,声明未提及。虽然这不改变主用途,但就描述准确性而言,存在实质性不一致,因此应判定为 mismatch。

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

技能说明提到音频文件会保存到 OSS 且自定义声音会永久保存到数据库,但未在上传步骤和显著位置明确警示:用户音频及其文本内容会被发送到外部服务并长期存储。这会使用户在不了解数据流向、保存期限和第三方处理的情况下上传可能含有敏感语音、生物特征或个人信息的数据,带来隐私泄露与合规风险。

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This file presents its natural-language description and user interaction phrases exclusively in Chinese, such as the module docstring and usage prompts. Under the policy, forcing a specific language without user opt-in can be a locale-policy violation when no alternative or justification is provided.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill reads a credential from ~/.openclaw/config.json, accessing sensitive data outside the declared TTS functionality. Even if done for legitimate API authentication, this creates an undeclared secret-access capability and broadens trust assumptions for the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The generate_speech function transmits user-provided text and selected voice reference data to a third-party API without explicit disclosure in this code path. This can expose sensitive text content and voice identifiers to an external service, especially if users assume processing is local.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The upload_audio path sends a local file to a remote service, but this file does not provide a clear user-facing disclosure at the point of use that local content will leave the machine. This is dangerous because users may provide sensitive voice recordings or unintended files without informed consent about external transmission.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains natural-language instructions and CLI output that assume Chinese as the only language for interaction. The policy requires flagging language or locale constraints when the skill forces a specific language without user opt-in or clear justification.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes TTS-related functions such as listing voices, selecting voices, uploading custom audio, generating speech, and checking task status. This method additionally calls a user info endpoint to retrieve the username associated with the API key, which is not mentioned in the stated skill scope and is not obviously required for the advertised TTS functionality.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

该技能的触发方式包含“直接转换”“提供文本内容要求转换成语音”等宽泛自然语言条件,容易在普通对话中被误触发,导致未经用户明确确认就调用外部 TTS 服务。由于该技能还涉及计费、外部 API 调用和可能上传用户提供内容,误触发会带来成本、隐私和操作越权风险。

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill reads a Datamass API key from the user's home-directory config file, which is a sensitive capability not visible from the TTS-focused interface shown here. Even if used for legitimate authentication, accessing local credentials expands the trust boundary and can surprise users or platform operators if the manifest does not clearly disclose this behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The generate_speech function transmits user-provided text and selected voice metadata to an external API without an explicit disclosure in this file. User text may contain confidential or regulated information, so sending it off-device to a third party without transparent notice can violate privacy expectations and policy requirements.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The upload_audio flow sends a local file path's contents to a remote TTS service, but this file does not present a clear user-facing disclosure or consent checkpoint before transmission. Because local files may contain sensitive voice data or unexpected content, silent exfiltration to a third-party service creates a real privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The generate flow transmits user-provided text and voice metadata to a remote service without explicitly informing the user at execution time. If the text contains secrets, personal data, or regulated content, users may unknowingly send sensitive material to a third party, which is a privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The upload command sends a local audio file to a remote TTS service, but the script provides no explicit disclosure, confirmation prompt, or privacy warning at the point of transfer. In a CLI skill that may be invoked by an agent or user without full awareness of backend data handling, this can lead to unintended exfiltration of sensitive audio content or embedded personal information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This method reads an arbitrary local file path and uploads the audio contents to a remote backend/OSS service using the caller's API key, with no in-code confirmation, validation, or restriction on what local file may be sent. In an agent context, this creates a real data-exfiltration risk if an untrusted prompt, tool chain, or caller supplies a sensitive local path or uploads audio containing private information without explicit user awareness.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill supports uploading reference audio and states that generated audio is stored in OSS for 24 hours, but it does not warn users about privacy, retention, or third-party handling of uploaded and generated data. This can lead users to submit sensitive voice samples or personal content without informed consent, creating privacy and compliance risk.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases are broad enough to match common requests involving speaking, TTS, or converting text, which can cause the skill to activate outside the user's intended scope. In an agent environment, overbroad activation increases the chance of unintended third-party API calls, billing events, or accidental processing of user-provided content and audio.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest describes TTS-focused capabilities such as browsing voices, uploading custom audio, generating speech, and checking task status. However, this code also calls a separate /sys/openapi/user/info endpoint to fetch and cache the account username, which is not mentioned in the stated skill scope and is not required by the surrounding code because other methods rely on header-based authentication.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

upload_audio() reads an arbitrary local file path and transmits the file contents to a remote backend/OSS service using the caller’s API key. In an agent setting, this is dangerous because it can cause unintended exfiltration of local files if the path is influenced by untrusted input and there is no in-code consent, path restriction, or file type validation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The service retrieves authenticated user profile information from /sys/openapi/user/info even though the advertised skill functionality is TTS generation and voice management. This creates unnecessary access to identity data, expanding the data exposure surface and violating data-minimization expectations if the username is not strictly required for the user-facing feature.

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

python
"""发送 POST 请求"""
        url = f"{self.base_url}{endpoint}"
        try:
            response = requests.post(url, json=data, headers=self.headers, timeout=self.timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:

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 · tts-voice-generator/scripts/tts_service.py (reported line 63)May include surrounding context.

python
"""发送 POST 请求"""
        url = f"{self.base_url}{endpoint}"
        try:
            response = requests.post(url, json=data, headers=self.headers, timeout=self.timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:

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 · tts-voice-generator/tts_service.py (reported line 63)May include surrounding context.

python
"""发送 POST 请求"""
        url = f"{self.base_url}{endpoint}"
        try:
            response = requests.post(url, json=data, headers=self.headers, timeout=self.timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:

Static analysis

No suspicious patterns detected.