Back to skill

Security audit

FlowVoice — Clone Any Voice From a Short Audio Sample

Security checks for vulnerabilities and agentic risk

Overview

This voice-cloning skill is not clearly malicious, but it needs Review because it can clone and store voices with weak safeguards and has a local code-execution risk in saved voice profiles.

Install only if you are comfortable reviewing and containing a voice-cloning tool. Use it only with voices you have permission to clone, avoid downloading third-party clips unless rights and consent are clear, do not load profile names or .pkl files from untrusted sources, and prefer running it in a sandbox until profile serialization and path validation are fixed.

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/flow_voice.py:78
Finding

Unsafe Pickle Deserialization Through Traversable Profile Path

Content
View full analysis

Vulnerability Details

File Location: scripts/flow_voice.py, lines 78–86
Vulnerability Type: Unsafe deserialization combined with path traversal
Risk Level: High

Vulnerable Code

python
def load_profile(name: str) -> object:
    """Load a saved voice profile."""
    path = PROFILES_DIR / f"{name}.pkl"
    if not path.exists():
        print(f"  ❌ No profile found for '{name}' at {path}", file=sys.stderr)
        print(f"  💡 Clone a voice first: --sample ref.wav --name {name}", file=sys.stderr)
        sys.exit(1)
    with open(path, "rb") as f:
        encoded = pickle.load(f)
    print(f"  ✅ Profile loaded: {name}")
    return encoded

Technical Analysis

The --voice argument is inserted directly into a path without restricting path separators, parent-directory components, absolute paths, or other special path syntax. Appending .pkl does not prevent traversal. For example, a profile name containing ../../../../tmp/payload can resolve outside PROFILES_DIR.

The selected file is then passed to pickle.load(). Python pickle is an executable serialization format: a crafted pickle can invoke attacker-selected callables during deserialization. Consequently, loading a profile is not merely a data operation and must only be performed on trusted, integrity-protected files.

The combination of attacker-selectable file resolution and unsafe deserialization creates a direct local code-execution primitive when an attacker can supply the --voice value and make a malicious pickle accessible to the process.

Attack Path

  1. An attacker creates or places a malicious pickle file at a location readable by the Skill process, such as /tmp/payload.pkl.
  2. The attacker invokes the Skill, or induces the Agent to invoke it, with a traversal value such as:
    text
    --voice ../../../../tmp/payload
    
  3. PROFILES_DIR / f"{name}.pkl" resolves to the external malicious file.

...[truncated 855 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace pickle with a non-executable serialization format supported by the model representation, such as a strictly validated tensor or structured data format.
  2. If pickle cannot be removed, treat profile files as trusted executable artifacts and never load files based on unrestricted user paths.
  3. Restrict profile identifiers with an allowlist:
    python
    import re
    
    if not re.fullmatch(r"[A-Za-z0-9_-]+", name):
        raise ValueError("Invalid profile name")
    
  4. Resolve the candidate path and enforce containment:
    python
    profiles_root = PROFILES_DIR.resolve()
    path = (profiles_root / f"{name}.pkl").resolve()
    
    if path.parent != profiles_root:
        raise ValueError("Profile path escapes the profile directory")
    
  5. Ensure the profile directory and files are owned by the Agent account and are not writable by untrusted users.
  6. Consider signing profiles or storing an integrity hash in trusted metadata before loading them.
  7. Run voice processing in a sandbox with minimal filesystem, network, and subprocess permissions to limit the impact of any deserialization compromise.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/flow_voice.py:68
Finding

Profile Name Path Traversal Allows Writes Outside the Profile Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/flow_voice.py, lines 68–74
Vulnerability Type: Path traversal and unrestricted file write
Risk Level: Medium

Vulnerable Code

python
def save_profile(encoded, name: str) -> Path:
    """Save an encoded voice profile."""
    PROFILES_DIR.mkdir(parents=True, exist_ok=True)
    path = PROFILES_DIR / f"{name}.pkl"
    with open(path, "wb") as f:
        pickle.dump(encoded, f)
    print(f"  💾 Profile saved: {path}")
    return path

Technical Analysis

The user-controlled --name argument is concatenated into a filesystem path without validation. pathlib.Path does not automatically constrain a joined path to its intended parent directory. Parent-directory components in name can therefore cause the resulting path to resolve outside PROFILES_DIR.

The destination is opened with wb, which creates the target if it does not exist and truncates it if it does. The effective filename must end in .pkl, which limits possible targets, but an attacker can still create or replace writable .pkl files outside the intended profile directory.

Attack Path

  1. The attacker identifies a writable location or existing writable .pkl file accessible to the Agent account.
  2. The attacker supplies a traversal value through --name, for example:
    text
    --name ../../../../tmp/attacker-selected
    
  3. The Skill encodes the supplied voice sample.
  4. save_profile() resolves the destination outside PROFILES_DIR.
  5. Opening the path with wb creates or truncates the external .pkl file.
  6. Serialized profile content is written at the attacker-selected location.

This write could also be combined with another component that automatically deserializes pickle files from that location, although such a component is not present in the audited project.

Impact Assessment

Exploitation allows creation or replacement of .pkl fil ...[truncated 454 chars]

Remediation
View remediation

Remediation Suggestions

  1. Apply the same strict allowlist to both --name and --voice, permitting only simple profile identifiers such as letters, digits, underscores, and hyphens.
  2. Resolve the profile root and candidate destination before opening the file, then verify that the candidate's parent is exactly the trusted profile directory.
  3. Reject absolute paths, path separators, . components, and .. components explicitly.
  4. Where replacement is unnecessary, use exclusive creation mode to prevent silent truncation:
    python
    with open(path, "xb") as f:
        pickle.dump(encoded, f)
    
  5. If profile replacement is required, request explicit confirmation and use an atomic temporary-file-and-rename workflow.
  6. Set restrictive directory and file permissions so other local users cannot modify or redirect profile paths.
  7. Add tests covering Unix and platform-specific traversal syntax, absolute paths, nested names, and symbolic-link behavior.

T08 · Insecure Dependencies

Warning
Location
scripts/flow_voice.py:19
Finding

Unpinned Python Dependencies and Mutable Model Artifact

Content
View full analysis

Vulnerability Details

File Location: scripts/flow_voice.py, lines 19–25 and line 52; also declared in SKILL.md, lines 10–11
Vulnerability Type: Unpinned third-party dependencies and model supply-chain exposure
Risk Level: Medium

Vulnerable Code

python
# /// script
# requires-python = ">=3.10"
# dependencies = [
#   "zipvoice",
#   "soundfile",
#   "librosa",
#   "numpy",
# ]
# ///
python
from zipvoice.luxvoice import LuxTTS
lux = LuxTTS("YatharthS/LuxTTS", device=device)

The Skill metadata repeats unpinned package declarations:

yaml
requires:
  bins: ["uv", "ffmpeg"]
  pip: ["zipvoice", "soundfile", "librosa"]

Technical Analysis

The Python dependencies are specified without exact versions or integrity hashes. A future uv run can therefore resolve package releases that differ from those reviewed during this audit. Installation and import of Python packages can execute package-controlled code, making dependency resolution part of the Skill's code-execution boundary.

The model is identified as YatharthS/LuxTTS without an immutable revision in the audited call. The documentation states that the model is downloaded on first use. If the underlying loader resolves the current repository state, model behavior or associated artifacts may change after the Skill itself has been reviewed.

No evidence was found that the named packages or model are currently malicious. The vulnerability is the lack of reproducibility and integrity controls, which increases exposure to upstream compromise, account takeover, malicious releases, and unexpected breaking changes.

Attack Path

  1. An upstream package publisher account or model repository is compromised, or a malicious/incompatible release is published.
  2. A user executes the Skill in a fresh environment or after caches are cleared.
  3. uv resolves an unpinned dependency version, or the model loader ret ...[truncated 1055 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin every Python dependency to an exact reviewed version.
  2. Generate and commit a lockfile containing cryptographic hashes, and require deployments to install strictly from that lockfile.
  3. Use a trusted package index explicitly and disable unintended fallback indexes to reduce dependency-confusion exposure.
  4. Review transitive dependencies and automate vulnerability and provenance scanning.
  5. Pin YatharthS/LuxTTS to an immutable reviewed commit or revision if supported by LuxTTS.
  6. Verify downloaded model artifacts against approved cryptographic hashes.
  7. Disable remote custom code unless it is strictly required and separately reviewed.
  8. Cache approved artifacts in a controlled internal repository to prevent silent upstream changes.
  9. Document the reviewed dependency and model versions in SKILL.md so metadata and executable declarations remain consistent.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This skill is specifically designed to clone a person's voice from short recordings, including third-party recordings, yet it provides no warning or safeguard around consent, impersonation, privacy, or local-law/compliance concerns. In context, that omission is dangerous because voice cloning materially enables fraud, social engineering, harassment, and unauthorized biometric-like use of someone's likeness.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documents and relies on shell-capable operations (uv, ffmpeg, and even yt-dlp) but does not declare any explicit tool scope or permissions. That creates a trust and containment gap: an agent may invoke shell commands for local file processing and remote downloads without a clear policy boundary, increasing the chance of unintended command execution or overbroad access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documented clone voice from URL flow encourages downloading and processing remote audio but omits warnings about third-party content rights, privacy implications, and the fact that fetching URLs may expose user/network metadata or pull untrusted content into the local environment. In a voice-cloning skill, this is especially risky because it lowers the barrier to cloning voices from scraped or unauthorized online media.

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

pickle.load deserializes arbitrary Python objects and can execute attacker-controlled code during loading. Because profile names map directly to files under a predictable per-user directory, any malicious or tampered .pkl placed there will trigger code execution when --voice is used.

Content

Scanner excerpt · scripts/flow_voice.py (reported line 90)May include surrounding context.

python
print(f"  💡 Clone a voice first: --sample ref.wav --name {name}", file=sys.stderr)
        sys.exit(1)
    with open(path, "rb") as f:
        encoded = pickle.load(f)
    print(f"  ✅ Profile loaded: {name}")
    return encoded

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

python
"""Bake voiceover audio into a video using ffmpeg."""
    print(f"  🎬 Baking audio into video: {video_path}")
    output_path.parent.mkdir(parents=True, exist_ok=True)
    result = subprocess.run(
        [
            "ffmpeg", "-y",
            "-i", video_path,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This skill performs voice cloning and optionally stores reusable voice profiles, which are biometric-like artifacts, without any consent, authorization, or privacy warning. In the context of a voice-cloning tool, that omission materially increases the risk of impersonation, misuse of another person's voice, and retention of sensitive identity-linked data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The ffmpeg invocation uses the -y flag, which forces overwrite of the output file if it already exists. While the script logs that it is baking audio into video, it does not warn that an existing target file may be replaced without confirmation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.