Back to skill

Security audit

Qwen Audio

Security checks for vulnerabilities and agentic risk

Overview

This audio skill mostly matches its TTS/STT purpose, but it has review-worthy risks around automatic package installation, default third-party model retrieval, and unsafe voice-profile path handling.

Install only if you are comfortable with an audio tool that can download models, use a third-party model mirror by default, modify its Python environment on macOS, and persist voice samples and transcripts. Review or patch the voice ID path handling before using custom voice IDs, avoid cloning voices without clear permission, and prefer pinned dependencies and an explicit official model endpoint.

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/qwen-audio.py:747
Finding

Unsanitized Voice IDs Permit Filesystem Path Traversal

Content
View full analysis
dict | None: """Get voice file paths by voice_id. Returns dict with ref_audio, ref_text and instruct, or None if not found.""" voices_dir = get_voices_dir() voice_dir = os.path.join(voices_dir, voice_id) if not os.path.isdir(voice_dir): return None ref_audio = os.path.join(voice_dir, "ref_audio.wav") ref_text_path = os.path.join(voice_dir, "ref_text.txt") instruct_path = os.path.join(voice_dir, "ref_instruct.txt") if not os.path.exists(ref_audio): return None ref_text = "" if os.path.exists(ref_text_path): with open(ref_text_path, "r", encoding="utf-8") as f: ref_text = f.read().strip() ``` ```python def run_voice_create(args: argparse.Namespace) -> None: """Create a new voice by generating audio and saving reference info.""" voices_dir = get_voices_dir() voice_id = args.id or str(uuid.uuid4())[:8] voice_dir = os.path.join(voices_dir, voice_id) os.makedirs(voice_dir, exist_ok=True) # Normalize text: replace newlines with spaces text = _normalize_text(args.text) instruct = _normalize_text(args.instruct) if args.instruct else None if not instruct: raise ValueError("--instruct 参数是必需的,请提供语音风格描述") # Generate audio using TTS output_audio = os.path.join(voice_dir, "ref_audio.wav") ``` ```python sf.write(output_audio, audio_np, sample_rate) # Save reference text ref_text_path = os.path.join(voice_dir, "ref_text.txt") with open(ref_text_path, "w", encoding="utf-8") as f: f.write(text) # Save instruct instruct_path = os.path.join(voice_dir, "ref_instruct.txt") with open(instruct_path, "w", encoding="utf-8") as f: f. ...[truncated 2228 chars]
Remediation
View remediation
str: if not VOICE_ID_RE.fullmatch(voice_id): raise ValueError("Invalid voice ID") return voice_id ``` 2. Resolve and verify directory containment before every read or write: ```python voices_root = os.path.realpath(get_voices_dir()) voice_dir = os.path.realpath(os.path.join(voices_root, validate_voice_id(voice_id))) if os.path.commonpath([voices_root, voice_dir]) != voices_root: raise ValueError("Voice path escapes the voices directory") ``` 3. Reject absolute paths, path separators, `.` components, and `..` components even if an allowlist is introduced. 4. Apply the same centralized path-validation function to both `run_voice_create()` and `get_voice_path()`. 5. Refuse to overwrite an existing voice directory unless the user explicitly supplies a dedicated overwrite option. 6. Where supported, protect against symbolic-link traversal by checking each path component and using safe file-opening primitives with no-follow semantics. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/qwen-audio.py:37
Finding

Unpinned Dependencies and Automatic Runtime Package Installation

Content
View full analysis
None: global _MLX_AUDIO_READY if _MLX_AUDIO_READY: return try: import mlx_audio # noqa: F401 except ImportError: print("mlx-audio 未安装,正在安装...", file=sys.stderr) os.system("uv add mlx-audio --prerelease=allow") import mlx_audio # noqa: F401 print("mlx-audio installed", file=sys.stderr) _MLX_AUDIO_READY = True ``` ```toml dependencies = [ "mlx-audio>=0.3.1; platform_system == 'Darwin'", "qwen-asr; platform_system != 'Darwin'", "qwen-tts>=0.1.1; platform_system != 'Darwin'", "torch; platform_system != 'Darwin'" ] ``` The mandatory environment checklist also instructs users to execute: ```text uv sync --prerelease=allow uv add mlx-audio --prerelease=allow ``` ### Technical Analysis Several executable dependencies are not pinned to exact reviewed versions. In particular, `qwen-asr` and `torch` have no version constraints, while `mlx-audio` and `qwen-tts` permit any later compatible release. The installation guidance additionally permits prerelease packages. On macOS, `_ensure_mlx_audio()` modifies the project environment during normal Skill execution by invoking `uv add`. The command's arguments are static, so this is not a shell-command injection issue. The risk instead arises because package resolution and installation occur at runtime using mutable remote package sources. Third-party Python packages execute code when imported and may also run build-related code during installation. Therefore, a compromised, malicious, or unexpectedly changed release can execute with the same privileges as the Skill. The code also ignores the return status from `os.system()` and immediat ...[truncated 1177 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/qwen-audio.py:198
Finding

Third-Party Hugging Face Mirror Is Enabled by Default for Model Retrieval

Content
View full analysis
bool: url = endpoint.rstrip("/") if not url.startswith(("http://", "https://")): url = f"https://{url}" try: req = urllib.request.Request(url, method="HEAD") with urllib.request.urlopen(req, timeout=timeout_sec): return True except urllib.error.HTTPError: return True except Exception: return False def _configure_hf(args: argparse.Namespace) -> None: if args.hf_mirror: os.environ["HF_ENDPOINT"] = "https://hf-mirror.com" endpoint = os.environ["HF_ENDPOINT"] else: endpoint = os.environ.get("HF_ENDPOINT", "https://huggingface.co") if not _can_reach_hf(endpoint): os.environ["HF_HUB_OFFLINE"] = "1" print("! 无法连接 Hugging Face,已启用离线模式(仅使用本地模型)", file=sys.stderr) ``` ```python parser.add_argument( "--hf-mirror", dest="hf_mirror", action="store_true", help="使用 hf-mirror 镜像站(默认)", ) parser.add_argument( "--no-hf-mirror", dest="hf_mirror", action="store_false", help="不使用 hf-mirror 镜像站", ) parser.set_defaults(hf_mirror=True) ``` ```python args = parser.parse_args() _configure_hf(args) args.func(args) ``` Model loading then uses mutable model identifiers through `from_pretrained()`: ```python _QWEN_ASR_MODEL = Qwen3ASRModel.from_pretrained( model_name, dtype=torch_dtype, device_map=resolved_device, max_inference_batch_size=2, max_new_tokens=8192, forced_aligner=aligner_model, forced_aligner_kwargs={ "dtype": torch_dtype, "device_map": resolved_device, }, ) ``` ```python _QWEN ...[truncated 2498 chars]
Remediation
View remediation
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The documented purpose presents the skill as a straightforward audio library, but the observed behavior includes persistent on-disk profile management, subtitle generation, network reachability logic, and runtime package installation via shell. This mismatch can mislead users and security reviewers about the actual trust and execution model, especially because runtime dependency installation and network behavior materially expand the attack surface.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill automatically installs a dependency at runtime via a shell command when mlx-audio is missing. In a skill/plugin context this is especially risky because simply invoking the functionality can modify the host environment and execute unreviewed installer logic, expanding the attack surface to package repositories, mirrors, and local shell/PATH hijacking.

Content

No source excerpt is available for this finding.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The code invokes a shell command at runtime to install a package when an import fails. Executing package installation through os.system is dangerous because it implicitly trusts the runtime environment, PATH resolution, package indexes, and shell execution context, which can lead to arbitrary code execution or supply-chain compromise if a malicious package, wrapper, or environment is present.

Content

Scanner excerpt · scripts/qwen-audio.py (reported line 43)May include surrounding context.

python
import mlx_audio  # noqa: F401
    except ImportError:
        print("mlx-audio 未安装,正在安装...", file=sys.stderr)
        os.system("uv add mlx-audio --prerelease=allow")
        import mlx_audio  # noqa: F401
        print("mlx-audio installed", file=sys.stderr)
    _MLX_AUDIO_READY = True

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill exposes capabilities that imply shell, network, environment access, and file read/write behavior but does not declare any tool scope or permission boundaries in the manifest. This increases the risk of over-broad execution in a host agent environment, because consumers cannot tell what the skill may invoke or restrict it to least privilege.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The voice cloning feature instructs users to provide reference audio and transcript without any consent, authorization, or privacy safeguards. In this skill context, that omission is materially dangerous because the core functionality enables impersonation or unauthorized reuse of a person's voice using supplied samples.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Automatically installing packages without explicit confirmation is unsafe operational behavior and can surprise users by executing networked install logic and modifying the environment. In combination with shell invocation and unpinned dependency resolution, the lack of a confirmation gate materially increases the risk of unintended code execution and supply-chain exposure.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest says the skill is a high-performance audio library with TTS and STT, but this code also creates and maintains a persistent voices/ directory, generates stored voice assets, and enumerates previously saved voice profiles. That is broader than plain TTS/STT processing and represents additional stateful voice-management behavior not reflected in the description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The TTS command sets --language to English by default, which imposes a specific language choice when the user does not explicitly select one. This is a natural-language policy concern because the skill does not offer neutral auto-detection or require user opt-in for the language default.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The STT command sets --language to English by default, which imposes a specific language/locale assumption even when the user does not request it. The file does not provide an opt-in mechanism or documented justification for this enforced default.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The voice create subcommand defaults --language to English, creating a fixed language assumption for generated voice assets. This is a policy issue because the skill enforces a specific language setting without explicit user choice or justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The STT workflow writes transcriptions and subtitle files to disk but does not warn that input audio and output text may contain sensitive or private information. This can lead to accidental persistence or disclosure of confidential content, particularly when users save outputs to shared or predictable locations.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The provided skill manifest describes this skill as a high-performance audio library with text-to-speech and speech-to-text capabilities, but the package metadata in this file says only "Add your description here." This is a clear description-behavior mismatch at the intent/documentation level because the code dependencies indicate an audio/STT/TTS package while the declared description is effectively empty and non-descriptive.

Content

No source excerpt is available for this finding.

Unverifiable Dependency: torch has 16 known advisory(ies) (CVE-2025-2953 (PyTorch susceptible to local Denial of Service); CVE-2022-45907 (PyTorch vulnerable to arbitrary code execution); CVE-2025-32434 (PyTorch: `torch.load` with `weights_only=True` leads to remote code execution) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/qwen-audio.py (reported line 262)May include surrounding context.

python
# Best-effort sampling rate inference.
    for attr in ("sample_rate", "sr", "sampling_rate"):
        if hasattr(result, attr):
            return int(getattr(result, attr))
    for attr in ("sample_rate", "sr", "sampling_rate"):
        if hasattr(model, attr):
            return int(getattr(model, attr))

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/qwen-audio.py (reported line 265)May include surrounding context.

python
return int(getattr(result, attr))
    for attr in ("sample_rate", "sr", "sampling_rate"):
        if hasattr(model, attr):
            return int(getattr(model, attr))
    return 24000

def _normalize_text(text: str) -> str:

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The parser description at description="MLX-Audio CLI (TTS + STT)" presents the tool as only supporting text-to-speech and speech-to-text. However, the same CLI defines separate voice create and voice list commands that persist and manage voice profiles, which contradicts that stated scope.

Content

No source excerpt is available for this finding.