Back to skill

Security audit

TtsCN

Security checks for vulnerabilities and agentic risk

Overview

This Chinese text-to-speech skill is coherent, but it needs review because some provider endpoint settings can redirect API keys, text, and voice samples to arbitrary URLs.

Install only if you are comfortable sending text and, for cloning, voice samples to the selected TTS provider. Avoid setting MINIMAX_API_BASE or VOLCENGINE_TTS_ENDPOINT unless you fully trust the exact HTTPS endpoint, and prefer a virtual environment with pinned dependencies.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/clone.py:44
Finding

Unvalidated MiniMax API Base Can Redirect Credentials, Text, and Voice Samples

Content
View full analysis

Vulnerability Details

File Location: scripts/clone.py:44, 97-137; scripts/backends/minimax.py:32-35, 69
Vulnerability Type: Arbitrary credential-bearing endpoint override
Risk Level: High

The MINIMAX_API_BASE environment variable is used without validating its scheme, hostname, port, or destination. Requests sent to the resulting URL include the MiniMax bearer credential and sensitive user content.

Vulnerable Code

From scripts/clone.py:

python
MINIMAX_BASE = os.environ.get(
    "MINIMAX_API_BASE", "https://api.minimax.io/v1"
)
python
api_key = os.environ.get("MINIMAX_API_KEY")
if not api_key:
    raise SystemExit(emit_error(
        "auth_missing_env", "MINIMAX_API_KEY not set",
        backend="minimax", exit_code=EXIT_AUTH))
headers = {"Authorization": f"Bearer {api_key}"}

# Reference audio: local file preferred; URLs are downloaded first.
if re.match(r"https?://", audio):
    resp = requests.get(audio, timeout=120)
    resp.raise_for_status()
    content = resp.content
    filename = os.path.basename(audio.split("?")[0]) or "sample.wav"
else:
    if not os.path.isfile(audio):
        raise SystemExit(emit_error(
            "input_not_found", f"Audio file not found: {audio}",
            field="audio", exit_code=EXIT_VALIDATION))
    with open(audio, "rb") as f:
        content = f.read()
    filename = os.path.basename(audio)

print("  Uploading reference audio ...", file=sys.stderr)
r = requests.post(
    f"{MINIMAX_BASE}/files/upload",
    headers=headers,
    data={"purpose": "voice_clone"},
    files={"file": (filename, content)},
    timeout=300,
)
python
r = requests.post(
    f"{MINIMAX_BASE}/voice_clone",
    headers={**headers, "Content-Type": "application/json"},
    json={"file_id": file_id, "voice_id": vid},
    timeout=300,
)

From scripts/backends/minimax.py:

python
# Global site by
...[truncated 2684 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse MINIMAX_API_BASE with urllib.parse.urlparse.
  2. Require the https scheme.
  3. Allowlist the documented MiniMax hosts, such as the approved global and China API domains.
  4. Reject embedded user information, fragments, unexpected ports, IP-literal destinations, and hostnames outside the allowlist.
  5. Keep separate constants for the approved global and China endpoints and expose a constrained region selector instead of accepting an arbitrary URL.
  6. If custom endpoints are necessary for development, require an explicit unsafe-development option and display the exact destination before transmitting credentials or voice samples.
  7. Do not attach production credentials to custom endpoints by default.
  8. Add automated tests covering HTTP URLs, lookalike domains, subdomain confusion, user-information syntax, redirects, and unexpected ports.
  9. Consider disabling redirects for credential-bearing uploads or verify every redirect destination before forwarding authorization headers.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/backends/__init__.py:196
Finding

Unvalidated Doubao Endpoint Can Exfiltrate API Tokens and Synthesis Text

Content
View full analysis

Vulnerability Details

File Location: scripts/backends/__init__.py:196-200; scripts/backends/doubao.py:20-24, 40-72
Vulnerability Type: Arbitrary credential-bearing endpoint override
Risk Level: High

The Doubao backend accepts an unrestricted VOLCENGINE_TTS_ENDPOINT value and sends the Volcengine token, application ID, and user text to that destination.

Vulnerable Code

From scripts/backends/__init__.py:

python
elif name == "doubao":
    config["appid"] = os.environ["VOLCENGINE_APPID"]
    config["token"] = os.environ["VOLCENGINE_ACCESS_TOKEN"]
    config["cluster"] = os.environ.get("VOLCENGINE_CLUSTER", "volcano_tts")
    config["endpoint"] = os.environ.get(
        "VOLCENGINE_TTS_ENDPOINT",
        "https://openspeech.bytedance.com/api/v1/tts"
    )

From scripts/backends/doubao.py:

python
appid = config["appid"]
token = config["token"]
cluster = config.get("cluster", "volcano_tts")
voice = config.get("voice", "BV001_streaming")
endpoint = config.get(
    "endpoint", "https://openspeech.bytedance.com/api/v1/tts"
)
python
headers = {
    "Authorization": f"Bearer; {token}",
    "Content-Type": "application/json",
}

for i, chunk in enumerate(chunks):
    part_file = os.path.join(out_dir, f".tts_part_{i:04d}.wav")
    part_files.append(part_file)

    for attempt in range(1, 4):
        try:
            req_id = str(uuid.uuid4())
            payload = {
                "app": {
                    "appid": appid,
                    "token": token,
                    "cluster": cluster,
                },
                "user": {"uid": uid},
                "audio": {
                    "voice_type": voice,
                    "encoding": "wav",
                    "rate": sample_rate,
                    "speed_ratio": speed_ratio,
                    "volume_ratio": 1.0,
                    "pitch_ratio": 1
...[truncated 2242 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the arbitrary endpoint variable with a fixed provider endpoint or a constrained region/environment selector.
  2. Require HTTPS and allowlist the exact approved ByteDance or Volcengine hostnames.
  3. Reject URLs with embedded credentials, fragments, IP literals, nonstandard ports, or unapproved domains.
  4. Confirm whether the provider protocol genuinely requires the token in both the authorization header and JSON body. Remove the duplicate copy wherever possible.
  5. Prevent credential forwarding across redirects, or validate each redirect target before following it.
  6. Fail closed when endpoint validation fails.
  7. Add tests for plaintext HTTP, lookalike hostnames, suffix attacks, unexpected ports, redirects, and malformed URLs.
  8. Document that synthesis text is transmitted to the selected provider and avoid logging request bodies or credentials.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:299
Finding

Security-Sensitive Runtime Dependencies Are Installed Without Version or Hash Pinning

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:299-311; repeated in data/providers.json dependency metadata
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Medium

The installation instructions retrieve mutable latest versions of multiple third-party packages from the configured Python package index without version constraints or integrity hashes.

Vulnerable Code

bash
# Core (always needed)
pip install edge-tts  # For Edge (default, free)

# Optional backends — install only what you use
pip install dashscope
pip install requests
pip install azure-cognitiveservices-speech
pip install tencentcloud-sdk-python-tts
pip install baidu-aip chardet
pip install websocket-client

Equivalent unpinned installation commands are also stored in data/providers.json and exposed through the generated provider documentation and CLI metadata.

Technical Analysis

Each installation command allows pip to resolve whatever package version is current at installation time. No lock file, exact version, package hash, or reviewed dependency set is supplied.

These packages execute in a process that may have access to TTS provider credentials, private synthesis text, local input files, and biometric voice samples. A compromised upstream release, dependency-account takeover, malicious transitive dependency, or unexpected future release could therefore execute with the user's permissions and access this sensitive context.

The referenced package names are consistent with the documented providers, and the audit found no evidence that they are intentionally malicious or typosquatted. The risk arises from mutable, unauthenticated-at-the-project-level dependency resolution.

Attack Path

  1. A user or automated agent follows the documented pip install command.
  2. pip resolves the latest available package and its transitive dependencies from the configured package index.
  3. An upstream p ...[truncated 1117 chars]
Remediation
View remediation

Remediation Suggestions

  1. Provide a reviewed lock file or requirements files with exact package versions.
  2. Record package hashes and install with pip --require-hashes.
  3. Pin transitive dependencies as well as direct dependencies.
  4. Install dependencies in a dedicated virtual environment with minimum filesystem permissions.
  5. Use a trusted package index or controlled internal mirror.
  6. Add automated dependency scanning and scheduled review of pinned versions.
  7. Update dependencies through a controlled process that includes changelog review, integrity verification, and tests.
  8. Ensure the generated dependency commands in data/providers.json and documentation remain synchronized with the secured lock files.
  9. Avoid automatically executing installation commands without explicit user approval.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (82)

Tainted flow: 'payload' from os.environ.get (line 53, 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 · scripts/backends/doubao.py (reported line 72)May include surrounding context.

python
},
                }

                resp = requests.post(endpoint, headers=headers, json=payload, timeout=timeout_sec)
                resp.raise_for_status()
                data = resp.json()

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

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The request destination is derived from the MINIMAX_API_BASE environment variable and the code sends both sensitive text content and the Authorization bearer token to that URL. If the environment is tampered with, data and credentials can be exfiltrated to an attacker-controlled endpoint, which is especially significant in an agent or plugin execution environment.

Content

Scanner excerpt · scripts/backends/minimax.py (reported line 69)May include surrounding context.

python
payload_dict["group_id"] = group_id
            payload = json.dumps(payload_dict)

        resp = requests.post(url, headers=headers, data=payload, timeout=120)
        if resp.status_code != 200:
            raise RuntimeError(
                f"MiniMax API error {resp.status_code}: {resp.text[:300]}"

Tainted flow: 'headers' from os.environ.get (line 102, 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 · scripts/clone.py (reported line 120)May include surrounding context.

python
filename = os.path.basename(audio)

    print("  Uploading reference audio ...", file=sys.stderr)
    r = requests.post(f"{MINIMAX_BASE}/files/upload", headers=headers,
                      data={"purpose": "voice_clone"},
                      files={"file": (filename, content)}, timeout=300)
    if r.status_code != 200:

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The request target is built from MINIMAX_API_BASE, an untrusted environment variable, and the same call includes the Bearer API key in headers. If an attacker can influence the runtime environment, they can redirect requests to an attacker-controlled server and capture the credential and uploaded voice data; this is effectively SSRF plus secret exfiltration.

Content

Scanner excerpt · scripts/clone.py (reported line 136)May include surrounding context.

python
vid = voice_id or _minimax_voice_id(name)
    print(f"  Creating clone '{vid}' ...", file=sys.stderr)
    r = requests.post(f"{MINIMAX_BASE}/voice_clone",
                      headers={**headers, "Content-Type": "application/json"},
                      json={"file_id": file_id, "voice_id": vid}, timeout=300)
    if r.status_code != 200:

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises TTS synthesis, but also supports voice cloning, remote clone lifecycle operations, and persistent storage of cloned voice metadata. Voice cloning materially changes the privacy, consent, and misuse profile of the skill; if auto-invoked under a narrower TTS expectation, it could lead to unauthorized biometric/voice data processing or paid remote actions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises TTS synthesis, but also supports voice cloning, remote clone lifecycle operations, and persistent storage of cloned voice metadata. Voice cloning materially changes the privacy, consent, and misuse profile of the skill; if auto-invoked under a narrower TTS expectation, it could lead to unauthorized biometric/voice data processing or paid remote actions.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 53)May include surrounding context.

md
open docs/providers.html

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 75)May include surrounding context.

md
open docs/providers.html

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill exposes broad capabilities including shell, network, environment-variable access, and file read/write, but does not declare any explicit tool scope or permission boundaries. In an agent environment, this increases the chance of over-privileged execution, making unintended file access, credential use, or external requests possible if the skill is invoked automatically.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file presents the skill as a Chinese TTS tool and repeatedly frames operation around Chinese text and Chinese voices, but it does not explicitly offer the user a language/locale choice or state a justified policy reason for restricting use to Chinese. This can constitute a language/locale policy issue when the skill is auto-invoked for broader TTS requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The activation criteria are broad enough to trigger on general discussion of TTS providers, pricing, or related keywords, which can cause the skill to open local HTML pages or initiate shell-based workflows without strong user intent. In agent systems, over-broad triggers raise the risk of surprising actions and over-collection or over-processing relative to the user's request.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
89% confidence
Finding

The skill persists cloned voice records locally and supports reuse of those identifiers later, creating session persistence around sensitive biometric-adjacent voice artifacts. If stored insecurely or without clear retention controls, this can expose voice identity metadata, enable unintended reuse, or blur user expectations about how long cloned assets remain available.

Content

Scanner excerpt · SKILL.md (reported line 142)May include surrounding context.

md
## Voice Cloning (`clone` command)

Create a custom voice from reference audio, store it under a name, then use
the name anywhere `--voice` is accepted. Built-in for **minimax** (local file
OK, 10s-5min audio, paid: ~$1.5/voice global site or ¥9.9 on first use China
site; a new clone is TEMPORARY until its first real synthesis — use it within

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 171)May include surrounding context.

md
- MiniMax creation is paid — never run `clone create --platform minimax`
  without the user's explicit confirmation (the CLI enforces `--yes`).
- Only clone the user's own voice or one they are authorized to use — both
  platforms contractually prohibit cloning third parties without consent.
- Reference audio: clean single-speaker speech, no BGM, 10-20s is ideal.
- Named voices live in `~/.ttsCN.json` under `cloned_voices`.

Session Persistence

Medium
Category
Rogue Agent
Confidence
78% confidence
Finding

The optional config file stores user defaults in home or project directories, creating persistent state that can influence later runs without fresh user input. While not highly sensitive on its own, project-local config can surprise users, alter backend/provider selection, or cause unintended paid API usage if inherited across contexts.

Content

Scanner excerpt · SKILL.md (reported line 364)May include surrounding context.

Config File (Optional)

Create ~/.ttsCN.json for personal defaults, or .ttsCN.json in a project directory:

json
{

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This JSON manifest contains user-facing descriptive fields such as cost labels, setup labels, tags, voice labels, and usage descriptions largely in Chinese, but it does not indicate that the language is optional or region-specific. Under the policy rule, forcing a specific language/locale without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · docs/providers.html (reported line 432)May include surrounding context.

html
<div class="detail-item"><label>API 地址</label><code>https://dashscope.aliyuncs.com/api/v1</code></div>
          <div class="detail-item full-width"><label>环境变量</label><code>DASHSCOPE_API_KEY</code></div>
        </div>
        <div class="clone-box"><strong>🔊 声音克隆:</strong> 音色复刻免费(合成正常计费): 10-20s公网URL音频, 1000音色/账户, 1年不用过期; tts.py clone create --platform cosyvoice</div>
        <div class="key-box"><strong>🔑 获取 API Key:</strong> <a href="https://bailian.console.aliyun.com/" target="_blank">https://bailian.console.aliyun.com/</a></div>
        <div class="voice-list">
          <h3>推荐音色</h3>

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file is titled as a Chinese TTS provider comparison and consistently frames recommendations around Chinese usage, but it does not state that the skill is region-specific or offer an opt-in for this locale constraint. Under the policy, forcing a specific language or locale without user choice or clear justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · scripts/backends/__init__.py (reported line 64)May include surrounding context.

python
def _build_voices():
    """Build VOICES and VOICE_DESCRIPTIONS from providers.json.
    First occurrence of a voice ID wins (don't overwrite descriptions)."""
    voices = {}
    descriptions = {}
    for p in _PROVIDERS["backends"]:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Default voice selections are consistently set to Chinese voices, and the fallback default is also a Chinese voice. This imposes a specific language/locale by default without any visible opt-in, alternative selection prompt, or justification in the file.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/backends/__init__.py (reported line 174)May include surrounding context.

python
info = BACKENDS[name]
    mod_name, pkg_name, install_cmd = info["import"]
    try:
        __import__(mod_name)
    except ImportError as e:
        raise MissingPackageError(
            f"'{pkg_name}' not installed. Run: {install_cmd}",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This module reads multiple credential-related environment variables such as API keys, tokens, and secrets when building backend configuration. While missing variables raise exceptions, there is no user-facing warning, log, or explanatory comment disclosing that sensitive credentials will be accessed.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The SSML payload forces xml:lang="zh-CN", and the default voice on L19 is also Chinese, which imposes a specific language/locale choice in the skill behavior. The file does not show any user opt-in, language selection mechanism, or comment explaining a region-specific requirement, so this is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

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 · scripts/backends/azure.py (reported line 77)May include surrounding context.

python
with open(concat_list, "w", encoding="utf-8") as f:
            for pf in part_files:
                f.write(f"file '{os.path.basename(pf)}'\n")
        result = subprocess.run(
            ["ffmpeg", "-y", "-f", "concat", "-safe", "0",
             "-i", concat_list, "-c", "copy", output_file],
            capture_output=True, text=True, cwd=out_dir,

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 · scripts/backends/cosyvoice.py (reported line 97)May include surrounding context.

python
with open(concat_list, "w", encoding="utf-8") as f:
            for pf in part_files:
                f.write(f"file '{os.path.basename(pf)}'\n")
        result = subprocess.run(
            ["ffmpeg", "-y", "-f", "concat", "-safe", "0",
             "-i", concat_list, "-c", "copy", output_file],
            capture_output=True, text=True, cwd=out_dir,

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 · scripts/backends/doubao.py (reported line 130)May include surrounding context.

python
with open(concat_list, "w", encoding="utf-8") as f:
            for pf in part_files:
                f.write(f"file '{os.path.basename(pf)}'\n")
        result = subprocess.run(
            ["ffmpeg", "-y", "-f", "concat", "-safe", "0",
             "-i", concat_list, "-c", "copy", output_file],
            capture_output=True, text=True, cwd=out_dir,

Static analysis

No suspicious patterns detected.