Back to skill

Security audit

Qwen3-tts

Security checks for vulnerabilities and agentic risk

Overview

The skill does text-to-speech work, but it is advertised as local/offline while also shipping under-disclosed remote HTTP server/client modes that can expose user text and host resources.

Review this carefully before installing. Use it only if you are comfortable with model/package downloads and potential remote TTS operation. Do not run the HTTP server on 0.0.0.0 or install the LaunchAgent unless you add authentication, firewall/VPN restrictions, and preferably HTTPS. Avoid sending private text through --remote or QWEN_TTS_REMOTE unless you trust the endpoint and network path.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/server.py:120
Finding

Unauthenticated TTS Service Exposed on All Network Interfaces

Content
View full analysis
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/server.py:40
Finding

Unauthenticated Clients Can Select Arbitrary Models for Server-Side Retrieval and Loading

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tts.py:103
Finding

TTS Text and Voice Instructions Can Be Sent to Arbitrary Plaintext HTTP Endpoints

Content
View full analysis
Remediation
View remediation

T06 · System Persistence

Error
Location
MAC_SERVER.md:168
Finding

Persistent LaunchAgent Continuously Exposes the Unauthenticated Service

Content
View full analysis
Label com.qwen.tts ProgramArguments /Users/YOUR_USER/qwen-tts-server/bin/python3 /Users/YOUR_USER/qwen-tts-server/server.py --host 0.0.0.0 --port 8765 --preload RunAtLoad KeepAlive StandardOutPath /tmp/qwen-tts.log StandardErrorPath /tmp/qwen-tts.error.log ``` ```bash launchctl load ~/Library/LaunchAgents/com.qwen.tts.plist ``` ### Technical Analysis The documentation marks autostart as optional, and persistent operation can be a legitimate convenience for a remote TTS server. However, the provided LaunchAgent starts the service at login, binds it to every interface, and restarts it continuously through `KeepAlive`. Because the associated server has no authentication or rate limiting, persistence materially increases the exposure window. The service remains available across sessions and after crashes rather than only while an operator intentionally runs it. This exceeds the minimum privileges needed for the core local TTS function. The LaunchAgent is installed in the current user's `Library/LaunchAgents` directory and therefore does not itself obtain root privileges. ### Attack Path 1. A user follows the documented instructions and installs the plist. 2. The user runs `launchctl load`. 3. At lo ...[truncated 523 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:55
Finding

Third-Party Dependencies Are Installed Without Version or Integrity Pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The core purpose does align broadly with text-to-speech generation using Qwen TTS and instruction-controlled voice synthesis. However, the declared description presents this as a local/offline TTS skill centered on a specific 1.7B model, while the code actually implements a network-accessible HTTP service for TTS. That is a materially undeclared capability because it exposes functionality over an API, including health and speaker enumeration endpoints, and can listen on all interfaces. Additionally, the code defaults to and accepts arbitrary model names, with the default set to Qwen3-TTS-12Hz-0.6B-CustomVoice rather than the declared 1.7B model. These are meaningful behavior differences, even though they support the same general TTS domain.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code does perform text-to-speech-related work and supports voice description, language selection, and extra instructions, which broadly aligns with the TTS theme. However, the declared description emphasizes local/offline synthesis using a specific model and says it runs entirely offline after model download. The actual code does not load or run a local model at all; instead it requires a remote URL or environment variable and POSTs the synthesis request to a remote server. That is a material mismatch in primary deployment model and resource access. The code is therefore not accurately represented as an offline local TTS implementation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code substantially matches a text-to-speech purpose: it synthesizes speech from text, supports multiple languages, 9 named speakers, and instruction-based voice style control. However, the description says this is a local/offline alternative to cloud TTS that runs entirely offline after initial model download, while the code explicitly supports remote mode through --remote or QWEN_TTS_REMOTE and sends synthesis requests over the network. That is an undeclared capability and materially conflicts with the offline-only characterization. Additionally, the declared model is Qwen3-TTS-12Hz-1.7B-CustomVoice, but the script’s default model argument is Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice, which is a notable discrepancy in the represented model. Therefore the description does not fully and accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill metadata claims the TTS runs entirely offline after initial model download, but this script explicitly requires a remote server URL and sends synthesis requests over HTTP. That mismatch can mislead users into disclosing sensitive text under a false privacy assumption, creating a trust and data exposure vulnerability.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script transmits the full input text, language, voice description, and instructions to an arbitrary user-supplied remote URL. In the context of a purportedly local/offline TTS skill, this is especially dangerous because users may provide sensitive content expecting it to remain on-device, enabling unintended exfiltration to untrusted hosts.

Content

No source excerpt is available for this finding.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · scripts/tts.py (reported line 41)May include surrounding context.

python
# Always use venv if available and not already active
if not os.environ.get("QWEN_TTS_VENV_ACTIVE") and VENV_PYTHON.exists():
    os.environ["QWEN_TTS_VENV_ACTIVE"] = "1"
    os.execv(str(VENV_PYTHON), [str(VENV_PYTHON)] + sys.argv)

# Check for remote mode
REMOTE_URL = os.environ.get("QWEN_TTS_REMOTE")

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest frames this skill as local, offline text-to-speech, but this file instructs users to expose it as an HTTP server for remote use. Serving TTS over the network is a materially broader operating mode than purely local offline use and changes the trust boundary described by the manifest.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The inline documentation explicitly says to run the system 'as HTTP server ... for remote TTS,' while the manifest describes a local offline TTS skill. This is an active contradiction in stated intent rather than a mere omission of implementation detail.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The guide instructs users to bind the TTS server to 0.0.0.0, exposing it on all network interfaces without authentication or access-control guidance. This can allow any host on the reachable network to access the service, submit text, consume resources, and potentially access sensitive prompts or generated content if the server or surrounding environment is not otherwise protected.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documentation exposes the TTS service to the network and provides a concrete LAN IP and health-check URL, but does not warn about data exposure, unauthorized access, or the lack of authentication. In context, this makes the skill more dangerous because users may assume a 'local TTS' tool is private when they are actually publishing a service to other systems on the network.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The example sets QWEN_TTS_LANGUAGE to Italian, and later command examples also hard-code Italian usage. This constitutes a natural-language locale constraint in the skill documentation without opt-in, alternatives, or a stated region-specific justification.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 168)May include surrounding context.

Using launchd (Mac)

Create ~/Library/LaunchAgents/com.qwen.tts.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 172)May include surrounding context.

Using launchd (Mac)

Create ~/Library/LaunchAgents/com.qwen.tts.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 173)May include surrounding context.

Using launchd (Mac)

Create ~/Library/LaunchAgents/com.qwen.tts.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 196)May include surrounding context.

Using launchd (Mac)

Create ~/Library/LaunchAgents/com.qwen.tts.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 201)May include surrounding context.

Using launchd (Mac)

Create ~/Library/LaunchAgents/com.qwen.tts.plist:

xml
<?xml version="1.0" encoding="UTF-8"?>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 172)May include surrounding context.

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · MAC_SERVER.md (reported line 201)May include surrounding context.

Load:

bash
launchctl load ~/Library/LaunchAgents/com.qwen.tts.plist

Troubleshooting

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises and demonstrates shell execution, environment-variable usage, and network access (model download and optional mirror) but does not declare any explicit tool scope or permissions. This creates a transparency and policy-enforcement gap: a host may invoke the skill assuming it is purely local/content-only while it can perform system and network actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger text is broad enough to capture many generic audio-generation or voice-message requests, which can cause the skill to be selected in situations the user did not explicitly intend. Over-broad activation increases the chance of unexpected shell/network execution and makes downstream behavior less predictable in an agentic environment.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The request model exposes a caller-controlled model field and defaults to Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice, which conflicts with the manifest's claim of a fixed 1.7B local TTS model. Allowing arbitrary model selection can trigger unexpected remote downloads, larger resource consumption, and execution of unreviewed model assets, undermining the stated offline/local trust boundary.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The /tts endpoint lets any caller influence load_model(request.model), effectively turning a simple TTS API into a model-loading service. In context, this broadens attack surface by enabling denial of service through repeated model swaps, unbounded downloads, and use of unapproved models not necessary for normal text-to-speech behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The server listens on 0.0.0.0 by default, exposing unauthenticated endpoints to the local network or beyond depending on host firewall/NAT configuration. Because /tts performs expensive model inference and can load caller-selected models, network exposure materially increases risk of unauthorized use and denial of service.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file header and usage text describe this as a remote VoiceDesign client, which conflicts with the broader skill description of local offline TTS. In security-sensitive tooling, inconsistent documentation is dangerous because users and downstream agents may rely on the offline claim while actually invoking networked behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
97% confidence
Finding

This code performs external transmission via requests.post to a user-controlled remote server, sending potentially sensitive prompt content off-host. External transmission is not always vulnerable on its own, but here it is risky because the skill is marketed as offline and the endpoint is arbitrary, increasing the chance of privacy loss or misuse.

Content

Scanner excerpt · scripts/tts-voicedesign.py (reported line 47)May include surrounding context.

python
print(f"🎙️  Voice: {voice_desc[:60]}...", file=sys.stderr)
    
    try:
        response = requests.post(
            f"{remote_url}/tts",
            json={
                "text": text,

Static analysis

No suspicious patterns detected.