Back to skill

Security audit

cosyvoice-speech-synthesizer

Security checks for vulnerabilities and agentic risk

Overview

This is a straightforward cloud text-to-speech skill, with normal privacy caveats because it sends chosen text to Alibaba Cloud DashScope.

Install only if you are comfortable sending the text you synthesize to Alibaba Cloud DashScope under your account API key. Avoid using it for secrets, regulated data, or confidential business text unless that third-party processing is approved, and choose output paths carefully because the script writes the downloaded audio to the path you provide.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/synthesize.py:364
Finding
Unvalidated API-Provided Audio URL Enables Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/synthesize.py:364-381`, with the untrusted URL extracted and passed to the download function at `scripts/synthesize.py:590-611` **Vulnerability Type**: Server-Side Request Forgery through an unvalidated remote URL **Risk Level**: Medium ### Vulnerable Code ```python def download_audio(url: str, output_path: str) -> bool: """ 下载音频文件 Args: url: 音频文件 URL output_path: 保存路径 Returns: 是否下载成功 """ try: response = requests.get(url, timeout=120, stream=True) response.raise_for_status() with open(output_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: f.write(chunk) return True ``` The URL is obtained from the remote API response and passed directly to the vulnerable function: ```python audio_info = output.get("audio", {}) audio_url = audio_info.get("url") audio_id = audio_info.get("id") expires_at = audio_info.get("expires_at") ``` ```python if args.no_download: print(f"音频 URL: {audio_url}") else: print(f"正在下载音频到: {args.output}") if download_audio(audio_url, args.output): # 获取文件大小 file_size = os.path.getsize(args.output) ``` ### Technical Analysis The application treats `output.audio.url` from the DashScope API response as a trusted download location. It performs a network request with `requests.get()` without validating: - The URL scheme - The destination hostname - The resolved IP address - Whether the destination is loopback, link-local, private, reserved, or otherwise non-public - Redirect destinations - The response content type - The maximum permitted response size The `requests` library follows redirects by default. Consequently, validating only the initial URL would not be sufficient unless every redirect target were also checked. Although `urlparse` is imported ...[truncated 1928 chars]
Remediation
## Remediation Suggestions 1. **Require a valid HTTPS URL** - Reject missing values, non-string values, embedded credentials, fragments, and every scheme other than `https`. - Parse the URL with `urllib.parse.urlparse()` before making a request. 2. **Allowlist trusted download hosts** - Permit only the documented Alibaba Cloud or DashScope audio-storage domains. - Compare normalized hostnames exactly or against carefully defined subdomain boundaries; do not use substring matching. 3. **Block non-public destinations** - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, unspecified, and non-global IPv4 and IPv6 addresses. - Account for all returned addresses and DNS rebinding risks. 4. **Secure redirect handling** - Prefer `allow_redirects=False`. - If redirects are necessary, apply the complete scheme, hostname, and resolved-address validation process to every redirect target and enforce a small redirect limit. 5. **Constrain downloaded responses** - Verify that the response content type is an expected audio media type. - Enforce a strict maximum `Content-Length` and independently count streamed bytes to handle absent or false headers. - Abort and remove partial files when the limit is exceeded. 6. **Use safe file replacement** - Download to a temporary file in the intended destination directory. - Validate successful completion before atomically replacing the target. - Remove temporary or partial files on every failure path. 7. **Validate response structure** - Ensure `output.audio.url` exists and is a non-empty string before attempting the download. - Treat malformed API responses as errors rather than forwarding values directly to the network client.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (11)

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

Critical
Category
Data Flow
Content
del payload["parameters"]

    try:
        response = requests.post(
            API_ENDPOINT,
            headers=headers,
            json=payload,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
# 尝试移除描述性前缀
                clean_text = remove_instruction_prefix(text, keyword, category)
                return instruction, clean_text
        
        # 再检查正则模式
        for pattern, template in config["patterns"]:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
# 尝试移除描述性前缀
                clean_text = remove_instruction_prefix(text, keyword, category)
                return instruction, clean_text
        
        # 再检查正则模式
        for pattern, template in config["patterns"]:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
# 尝试移除描述性前缀
                clean_text = remove_instruction_prefix(text, keyword, category)
                return instruction, clean_text
        
        # 再检查正则模式
        for pattern, template in config["patterns"]:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents use of an API key and cloud synthesis service, implying environment access and network egress, but it does not declare any explicit tool scope or permission boundaries. This can cause the agent platform to invoke the skill without clear consent or least-privilege constraints, increasing the chance of unintended secret use or outbound transmission.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill encourages immediate use but does not clearly warn that submitted text is transmitted to a third-party cloud TTS API. Users may provide private, regulated, or proprietary text under the assumption processing is local, creating confidentiality and compliance risks.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The examples encourage broad trigger phrases like '合成今天天气怎么样?' and '用河南话说:...' without requiring a clear invocation boundary or confirming that user text will be sent to an external TTS provider. In an agent setting, ambiguous natural-language activation can cause accidental skill execution on ordinary conversation and unintended disclosure of user content to the service.

Vague Triggers

Medium
Confidence
86% confidence
Finding
The section describing that the tool 'smartly understands' natural language broadens activation to generic conversational phrases like emotion, dialect, or roleplay requests. This makes the skill more likely to engage on ambiguous user intent and send sensitive or unintended text externally without the user realizing a cloud API is involved.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The top-level docstring states the skill is a Chinese speech synthesis script and specifically supports extracting instructions from natural-language descriptions, while the implementation patterns only recognize Chinese text. This effectively imposes a language/locale constraint without any user choice or documented opt-in, which matches the language-policy violation criteria.

External Transmission

Medium
Category
Data Exfiltration
Content
del payload["parameters"]

    try:
        response = requests.post(
            API_ENDPOINT,
            headers=headers,
            json=payload,
Confidence
85% confidence
Finding
The script transmits user-provided text and optional instruction metadata to an external cloud API for synthesis. In this skill context, external transmission is core functionality, but it still matters because users may unknowingly send sensitive content off-host to a third party, especially with automatic instruction extraction and verbose console output that normalizes forwarding raw input.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
All descriptive and instructional text in the skill is written in Chinese, and the file does not indicate that this language choice is optional or tied to a region-specific requirement. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy issue.

Static analysis

No suspicious patterns detected.