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

Content
View full analysis

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
View 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)

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

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

md
---
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

Missing User Warnings

Medium
Category
Not specified by scanner
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.

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/qwen_audio.py (reported line 147)May include surrounding context.

python
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",

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/qwen_audio.py (reported line 194)May include surrounding context.

python
if args.rate:
        cmd.extend(["-r", str(args.rate)])
    cmd.append(text)
    subprocess.run(cmd, check=True)
    print("Played with macOS say")

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · scripts/qwen_audio.py (reported line 527)May include surrounding context.

python
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,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.