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## 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
## 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## 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
## 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## 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
## 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
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.

Lp3

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.

Missing User Warnings

Medium
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.

Insecure deserialization: pickle.load()

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

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""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,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
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.

Missing User Warnings

Low
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.

Static analysis

No suspicious patterns detected.