Back to skill

Security audit

qwencloud-audio-tts

Security checks for vulnerabilities and agentic risk

Overview

This TTS skill mostly matches its purpose, but it adds automatic update-management behavior and under-scoped credential/network handling that users should review before installing.

Review this skill before installing. Use it only if you are comfortable sending synthesis text and API credentials to QwenCloud/DashScope, keep .env and QWEN_BASE_URL under trusted control, and do not approve the update-check skill installation unless you have separately reviewed and trust that remote skill source. Be aware that ordinary TTS runs may execute an installed sibling update checker and write update-prompt state under .agents.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:224
Finding

Mandatory Update Workflow Hijacks Agent Responses

Content
View full analysis
The update-check skill is not installed. It keeps you informed about new qwencloud/qwencloud-ai features and bug fixes. > > 1) **Install now** — I'll run the install command for you > 2) **Skip for now** — do nothing this time > 3) **Never remind me** — permanently suppress this prompt After the user responds: - User chooses **1** → run: `npx skills add QwenCloud/qwencloud-ai --skill qwencloud-update-check -y`, then run: `python3 /gossamer.py --dismiss` - User chooses **2** → run: `python3 /gossamer.py --dismiss`, then continue normally - User chooses **3** → run: `python3 /gossamer.py --never-install`, then continue normally ### Signal: `[UPDATE_AVAILABLE]` When stderr contains this signal, you MUST append a brief update notice to your response, including the version info and the update command shown in the stderr output. ``` ### Technical Analysis The Skill declares text-to-speech synthesis as its purpose, but its instructions require the agent to inspect stderr and alter its response according to update-related signals. The repeated use of mandatory directives—such as “MUST act,” “MUST present,” and “wait for their response”—changes the agent’s immediate objective from completing a ...[truncated 1537 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/gossamer.py:110
Finding

Automatic Execution of Unverified Sibling-Skill Code

Content
View full analysis
None: """Emit update-check signals to stderr (non-blocking, failure-safe).""" if _run_update_signal_impl: try: _run_update_signal_impl(caller=caller or __file__) except Exception: pass ``` Normal TTS st ...[truncated 2070 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:237
Finding

Unpinned Remote Skill and Python Dependency Installation

Content
View full analysis
/gossamer.py --dismiss` ``` The same mutable command is constructed in code: ```python _SKILLS_REPO = "QwenCloud/qwencloud-ai" INSTALL_CMD = f"npx skills add {_SKILLS_REPO} --skill qwencloud-update-check -y" ``` CosyVoice installation uses a lower-bound-only dependency constraint: ```python Dependencies: pip install dashscope>=1.24.6 Or with venv: python3 -m venv .venv && source .venv/bin/activate && pip install dashscope>=1.24.6 ``` ### Technical Analysis The `npx skills add` instruction references a mutable repository identity rather than a reviewed commit, signed release, or content digest. The `-y` argument suppresses normal interaction, reducing the opportunity for the user to inspect the resolved source before installation. Similarly, `dashscope>=1.24.6` allows any future version satisfying the lower bound. No upper bound, exact version, lockfile, or package hash is supplied. A future compromised, malicious, or incompatible release could therefore be installed even though it was not part of the audited artifact. The DashScope SDK is relevant to the optional CosyVoice functionality, but unrestricted future-version installation and automatic promotion of another remote Skill are broader than necessary. ### Attack Path 1. The upstream repository, package publisher account, distribution channel, or a future release is compromised. 2. Malicious code is published under the same accepted repository or package identity. 3. The user follows the Skill’s installat ...[truncated 710 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/qwencloud_lib.py:255
Finding

API Credentials and User Text Can Be Forwarded to an Arbitrary Configured Endpoint

Content
View full analysis
str: url = os.getenv("QWEN_BASE_URL") region = os.getenv("QWEN_REGION", "ap-southeast-1").lower() if region == "none": # When QWEN_REGION=none, skip URL assembly and return the user-configured base URL as-is. if not url: raise RuntimeError( "QWEN_BASE_URL must be set when QWEN_REGION=none." ) return url.rstrip("/") if url: return url.rstrip("/") return self._COMPAT_BASE.get(region, self._COMPAT_BASE["ap-southeast-1"]) def native_base_url(self) -> str: custom = os.getenv("QWEN_BASE_URL") region = os.getenv("QWEN_REGION", "ap-southeast-1").lower() if region == "none": # When QWEN_REGION=none, skip URL assembly and return the user-configured base URL as-is. if not custom: raise RuntimeError( "QWEN_BASE_URL must be set when QWEN_REGION=none." ) return custom.rstrip("/") if custom: parsed = urllib.parse.urlparse(custom.rstrip("/")) return f"{parsed.scheme}://{parsed.netloc}/api/v1" return self._NATIVE_BASE.get(region, self._NATIVE_BASE["ap-southeast-1"]) ``` The provider then attaches the API key to requests: ```python def make_headers( self, api_key: str, payload: Any = None, ) -> dict[str, str]: hdrs: dict[str, str] = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}", } if payload and self.has_managed_url(payload): hdrs["X-DashScope-OssResourceResolve"] = "enable" source_cfg = build_source_config() if source_cfg: hdrs["X-Das ...[truncated 2572 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (44)

Tainted flow: 'req' from os.getenv (line 687, credential/environment) → urllib.request.urlopen (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/qwencloud_lib.py (reported line 349)May include surrounding context.

python
headers={"Content-Type": ct},
        )
        try:
            with urllib.request.urlopen(req, timeout=120) as resp:
                if resp.status != 200:
                    raise RuntimeError(f"OSS upload HTTP {resp.status}")
        except urllib.error.HTTPError as exc:

Tainted flow: 'req' from os.getenv (line 687, credential/environment) → urllib.request.urlopen (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/qwencloud_lib.py (reported line 458)May include surrounding context.

python
"Content-Type": "application/json",
            },
        )
        with urllib.request.urlopen(req, timeout=30) as resp:
            body = json.loads(resp.read().decode("utf-8"))
        data = body.get("data")
        if not data:

Tainted flow: 'req' from os.getenv (line 687, credential/environment) → urllib.request.urlopen (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/qwencloud_lib.py (reported line 689)May include surrounding context.

python
for attempt in range(retries + 1):
        req = urllib.request.Request(url, data=data, headers=hdrs, method=method)
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                return json.loads(resp.read().decode("utf-8"))
        except urllib.error.HTTPError as exc:
            body = ""

Tainted flow: 'req' from os.getenv (line 687, credential/environment) → urllib.request.urlopen (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/qwencloud_lib.py (reported line 745)May include surrounding context.

python
data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(url, data=data, headers=hdrs, method="POST")
    try:
        resp = urllib.request.urlopen(req, timeout=timeout)
    except urllib.error.HTTPError as exc:
        body = ""
        try:

Tainted flow: 'req' from os.getenv (line 687, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

download_file fetches an arbitrary URL and writes the response to an arbitrary destination path, with no hostname, scheme, size, or path restrictions. In an agent/skill context this can enable SSRF to internal services and unsafe file writes if upstream callers pass untrusted URL or destination values.

Content

Scanner excerpt · scripts/qwencloud_lib.py (reported line 841)May include surrounding context.

python
"""Download a file from *url* to *dest*, creating parent dirs as needed."""
    dest.parent.mkdir(parents=True, exist_ok=True)
    req = urllib.request.Request(url, headers={"User-Agent": "qwencloud-ai/1.0"})
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        dest.write_bytes(resp.read())
    return dest

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a text-to-speech helper but also instructs the agent to perform update prompting, install another skill, inspect local state, and execute separate subprocesses. This hidden expansion of behavior is dangerous because users and calling agents may authorize the skill expecting media generation, not environment modification and auxiliary management actions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as a text-to-speech helper but also instructs the agent to perform update prompting, install another skill, inspect local state, and execute separate subprocesses. This hidden expansion of behavior is dangerous because users and calling agents may authorize the skill expecting media generation, not environment modification and auxiliary management actions.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as a text-to-speech helper but also instructs the agent to perform update prompting, install another skill, inspect local state, and execute separate subprocesses. This hidden expansion of behavior is dangerous because users and calling agents may authorize the skill expecting media generation, not environment modification and auxiliary management actions.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/qwencloud_lib.py (reported line 87)May include surrounding context.

python
script_file : str or Path, optional
        ``__file__`` of the calling script, used to locate the repo root.
    """
    load_dotenv(Path.cwd() / ".env")
    origin = Path(script_file).resolve() if script_file else None
    if origin:
        repo = find_repo_root(origin)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/qwencloud_lib.py (reported line 92)May include surrounding context.

python
script_file : str or Path, optional
        ``__file__`` of the calling script, used to locate the repo root.
    """
    load_dotenv(Path.cwd() / ".env")
    origin = Path(script_file).resolve() if script_file else None
    if origin:
        repo = find_repo_root(origin)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/qwencloud_lib.py (reported line 406)May include surrounding context.

python
ak_id = os.getenv("OSS_ACCESS_KEY_ID", "")
            ak_secret = os.getenv("OSS_ACCESS_KEY_SECRET", "")
            credentials_provider = (
                oss.credentials.EnvironmentVariableCredentialsProvider()
            )

        cfg = oss.config.load_default()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/qwencloud_lib.py (reported line 590)May include surrounding context.

python
auto-detected from the API response URL.

environment variables:
  DASHSCOPE_API_KEY   (required) API key — also loaded from .env
  QWEN_API_KEY        (alternative) Alias for DASHSCOPE_API_KEY
  QWEN_REGION         ap-southeast-1 (default)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

python
auto-detected from the API response URL.

environment variables:
  DASHSCOPE_API_KEY   (required) API key — also loaded from .env
  QWEN_API_KEY        (alternative) Alias for DASHSCOPE_API_KEY
  QWEN_REGION         ap-southeast-1 (default)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill declares broad operational behavior involving environment access, filesystem reads/writes, network access, and shell execution, but does not declare any tool scope or permission boundaries. This increases the risk that an agent will execute high-impact actions without clear policy gating or user awareness, especially because the skill also includes installation and update workflows beyond core TTS use.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% 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 34)May include surrounding context.

md
**NEVER output any API key or credential in plaintext.** Always use variable references (`$DASHSCOPE_API_KEY` in shell, `os.environ["DASHSCOPE_API_KEY"]` in Python). Any check or detection of credentials must be **non-plaintext**: report only status (e.g. "set" / "not set", "valid" / "invalid"), never the value. Never display contents of `.env` or config files that may contain secrets.

**When the API key is not configured, NEVER ask the user to provide it directly.** Instead, help create a `.env` file with a placeholder (`DASHSCOPE_API_KEY=sk-your-key-here`) and instruct the user to replace it with their actual key from the [QwenCloud Console](https://home.qwencloud.com/api-keys). Only write the actual key value if the user explicitly requests it.

## Key Compatibility

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill documentation extends into installing and managing a separate update-check skill, which is outside the core responsibility of speech synthesis. This creates unnecessary attack surface and can normalize privileged environment changes triggered indirectly by routine TTS usage.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill includes package/skill installation and state-changing commands unrelated to core TTS functionality, including automatic follow-up commands after a user selects an option. Embedding these commands in an operational skill increases the chance that an agent modifies the local tooling environment when the user only intended to generate audio.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

The command npx skills add QwenCloud/qwencloud-ai --skill qwencloud-update-check -y invokes remote package/tool resolution without a pinned version, making behavior dependent on whatever version is current at execution time. This creates supply-chain and reproducibility risk: a later compromised or incompatible release could be installed automatically in response to a stderr signal.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The update-check workflow directs installation and state-changing commands that modify the local skill/tool environment, yet the warning is framed as a required post-execution flow rather than a clearly hazardous change. Users invoking TTS may not expect package installation or persistent configuration changes, creating consent and integrity risks.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The registry entry includes broad trigger terms such as "voice" and "narration," which are common in many unrelated user requests and can cause this skill to be invoked when the user did not intend text-to-speech. Over-broad activation increases the chance of incorrect tool selection, unintended file reads or config changes, and expansion of the skill's effective attack surface.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes this skill as synthesizing speech from text with Qwen TTS models. This reference guide additionally documents uploading user audio samples to create custom cloned voices, which is a separate capability involving voice enrollment from audio input rather than straightforward text-to-speech generation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The voice-cloning example instructs users to upload a local audio sample to a remote API without any privacy, consent, retention, or sensitivity warning. In this skill context, voice samples are biometric/identity-adjacent data, so omission of a warning can lead users to send third-party or sensitive recordings to an external service without informed consent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The guide explicitly instructs making outbound requests to an external TTS service, which transmits user text and an API credential off-host. In a TTS skill this behavior is expected, but it still has security significance because it enables external data transfer and should be clearly disclosed and constrained.

Content

Scanner excerpt · references/execution-guide.md (reported line 14)May include surrounding context.

md
3. **SSL errors** (`CERTIFICATE_VERIFY_FAILED`): On macOS, run `/Applications/Python\ 3.x/Install\ Certificates.command`. On Linux/Windows, set `SSL_CERT_FILE` to point to your CA bundle.
4. **Proxy**: Set `HTTPS_PROXY=http://proxy:port` before running the script.

After fixing, retry the script (Path 1). If the environment is unfixable, fall through to **Path 2 (curl)** below — curl is available on most systems without Python.

## Path 2 · Direct API Call (curl)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The guide instructs users to send input text and an API bearer token to a third-party cloud endpoint but does not warn that user-provided content leaves the local environment. This creates a real transparency and privacy risk because users may unknowingly transmit sensitive text or use privileged credentials without informed consent.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This file's behavior is unrelated to text-to-speech and instead manages update prompting, installation suggestions, and suppression state for another skill. That scope creep is dangerous because it introduces hidden operational behavior and expands the attack surface of a TTS skill beyond its declared purpose, making review and user consent harder.

Content

No source excerpt is available for this finding.