Back to skill

Security audit

qwen-audio-lab

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it sends speech content to Qwen and has an output-path weakness that could overwrite user-writable files.

Review this before installing if you plan to process private scripts, slide notes, or voice samples. Use only voice recordings you have consent to clone, assume Qwen-backed commands transmit content to Aliyun/Qwen, keep the API key scoped appropriately, and avoid passing absolute paths or ../ components to --output until the output-path validation is fixed.

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

Error
Location
scripts/qwen_audio.py:224
Finding
Arbitrary File Write Through Unsanitized Output Path## Vulnerability Details **File Location**: `scripts/qwen_audio.py`, lines 224–226; vulnerable write sink at lines 134–137 **Vulnerability Type**: Path traversal and unrestricted file write **Risk Level**: High The output filename supplied through `--output` is used without validating whether the resulting path remains inside the configured audio output directory. ```python def download_file(url: str, out_path: pathlib.Path) -> pathlib.Path: out_path.parent.mkdir(parents=True, exist_ok=True) with urllib.request.urlopen(url, timeout=120) as resp: out_path.write_bytes(resp.read()) return out_path ``` ```python if args.download: ext = args.format or "wav" filename = args.output or f"{sanitize_name(args.voice)}-{int(time.time())}.{ext}" path = output_dir() / filename download_file(audio_url, path) result["file"] = str(path) ``` ### Technical Analysis `args.output` is attacker-influenced command-line input. Unlike automatically generated filenames, this value is not passed through `sanitize_name()` or subjected to path-containment validation. In Python path handling, joining a base directory with an absolute path discards the base directory. Relative paths containing `../` can also traverse outside the intended directory. Consequently, values such as `../../target.wav` or `/absolute/path/target.wav` cause `download_file()` to write outside `QWEN_AUDIO_OUTPUT_DIR`. The sink creates missing parent directories with `mkdir(parents=True, exist_ok=True)` and then uses `write_bytes()`, which truncates and overwrites an existing destination. There is no check for an existing file, symbolic link, absolute path, traversal component, or resolved-path containment. The written content is the audio response downloaded from the Qwen service, so an attacker does not necessarily control arbitrary byte-for-byte content. Nevertheless, the destination is unrestricted within the operating-sys ...[truncated 1714 chars]
Remediation
## Remediation Suggestions 1. Accept only a basename for `--output`, rejecting absolute paths and directory components: ```python requested = pathlib.Path(args.output) if requested.is_absolute() or requested.name != args.output: fail("--output must be a filename without directory components.") ``` 2. Resolve and validate the final destination against the configured output directory: ```python base = output_dir().resolve() destination = (base / requested).resolve() try: destination.relative_to(base) except ValueError: fail("Output path must remain inside the configured output directory.") ``` 3. Validate containment after path resolution to prevent both `../` traversal and absolute-path bypasses. 4. Defend against symbolic-link attacks. Where feasible, reject symlink destinations and use secure file-opening semantics that prevent following symbolic links. 5. Avoid silent overwrites. Use exclusive file creation by default or require an explicit `--overwrite` option before replacing an existing file. 6. Apply the same centralized output-path validation to every command that accepts or derives an output filename, ensuring future commands cannot bypass the restriction.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises shell, filesystem, environment, and network-capable commands but does not declare any tool scope or permission boundaries. That increases the chance an agent will invoke powerful capabilities without explicit review, especially because the commands can read local files, write outputs/state, and send data to a remote service.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: qwen-audio-lab
description: Hybrid text-to-speech, reusable voice cloning, and narrated audio generation for macOS plus Aliyun Qwen. Use when the user wants to convert text into speech, clone and reuse a voice from a reference recording, generate narration files from plain text or text files, or create PPT speaker-note voiceovers.
---

# Qwen Audio Lab
Confidence
82% confidence
Finding
The skill is explicitly designed to clone, remember, and reuse voices across sessions, which introduces session persistence of potentially sensitive biometric-like identifiers and reusable assets. In context this appears to be product functionality rather than malicious behavior, but persistence increases the risk of unintended reuse, impersonation, or disclosure if state is not tightly controlled.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill description says it uses Aliyun Qwen, but it does not clearly warn that user-provided text, uploaded scripts, and reference audio for cloning are transmitted to a remote third-party service. This creates a privacy and data-handling risk because users may provide sensitive documents, speaker notes, or voice samples without informed consent.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if not shutil.which("ffmpeg"):
        fail("ffmpeg is required for prefix trimming but was not found in PATH.")
    tmp_path = path.with_name(path.stem + '.trimmed' + path.suffix)
    subprocess.run(
        [
            "ffmpeg", "-y", "-v", "error", "-i", str(path),
            "-af", f"atrim=start={seconds},afade=t=in:st=0:d=0.03",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if args.rate:
        cmd.extend(["-r", str(args.rate)])
    cmd.append(text)
    subprocess.run(cmd, check=True)
    print("Played with macOS say")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete-voice command sends a remote delete action for a custom voice and then removes the local record, but the code provides no confirmation prompt, warning print, or comment/docstring disclosing that this is destructive. Because deleting a custom voice may be irreversible and affects persisted remote resources, users should be explicitly warned before execution.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
else:
            concat = per_slide_dir / f"slide-{slide_no:02d}-concat.txt"
            concat.write_text("".join([f"file '{f.as_posix()}'\n" for f in chunk_files]), encoding="utf-8")
            subprocess.run(
                ["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", str(concat), "-c", "copy", str(final)],
                check=True,
                stdout=subprocess.DEVNULL,
Confidence
83% confidence
Finding
The code generates an ffmpeg concat manifest by embedding file paths into lines like `file '...` without escaping single quotes or other concat-demuxer metacharacters. If an attacker can influence the PPT filename or output directory path to include a quote, ffmpeg may misparse the manifest, potentially concatenating unintended files or causing denial of service; the use of `-safe 0` further reduces ffmpeg's path safety checks.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Several CLI commands set --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The speak-last-cloned command sets --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The narrate-text command sets --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The narrate-file command sets --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The ppt-own-voice command sets --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The narrate-ppt command sets --language-type to "Chinese" by default, which imposes a specific language choice unless the user overrides it. This is a natural-language locale policy concern because the tool forces a language/locale preference rather than asking the user to choose or making the behavior neutral.

Missing User Warnings

Low
Confidence
93% confidence
Finding
The skill notes default output and state directories, but it does not clearly warn users that generated audio files and remembered voice metadata/state are persisted on disk by default. This can expose sensitive narration content or reusable voice identifiers to other local users, backups, or later unintended reuse.

Static analysis

No suspicious patterns detected.