Back to skill

Security audit

Voice.ai: Creator Voiceover Forge

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it has review-worthy risks around redirectable API requests and unsafe generated helper scripts.

Review before installing. Use only a trusted environment and .env file, leave VOICEAI_API_BASE unset unless you fully trust the endpoint, avoid sensitive scripts unless you are comfortable sending them to Voice.ai, and do not run generated ffmpeg helper scripts for untrusted filenames or output paths.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/api.ts:291
Finding
Authenticated API requests can be redirected to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `src/api.ts:291`, `src/api.ts:314-319`, and `src/api.ts:410-428` **Bundled Location**: `voiceai-vo.cjs:4333`, `voiceai-vo.cjs:4348-4353`, and `voiceai-vo.cjs:4410-4426` **Vulnerability Type**: Unvalidated service endpoint override causing credential and data disclosure **Risk Level**: High ### Vulnerable Code ```ts constructor(options: { apiKey?: string; mock?: boolean }) { this.apiKey = options.apiKey ?? null; this.mock = options.mock ?? false; this.baseUrl = process.env.VOICEAI_API_BASE ?? BASE_URL; } private endpoint(path: string): string { return `${this.baseUrl}/api/${API_VERSION}${path}`; } ``` The voice-list request forwards the bearer credential to the selected endpoint: ```ts const url = `${this.endpoint('/tts/voices')}?${params.toString()}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${this.apiKey}`, 'User-Agent': 'voiceai-creator-voiceover-pipeline/0.1.0', }, }); ``` The TTS request forwards both the bearer credential and script content: ```ts const res = await fetch(this.endpoint('/tts/speech'), { method: 'POST', headers: { Authorization: `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'User-Agent': 'voiceai-creator-voiceover-pipeline/0.1.0', }, body: JSON.stringify(body), }); if (!res.ok) { const errBody = await res.text().catch(() => ''); if (res.status === 401) throw new Error('Voice.ai: Invalid or missing API key (401).'); if (res.status === 402) throw new Error('Voice.ai: Insufficient credits (402). Check your dashboard.'); if (res.status === 429) throw new Error('Voice.ai: Rate limited (429). Wait and retry.'); throw new Error(`Voice.ai TTS error ${res.status}: ${errBody}`); } return Buffer.from(await res.arrayBuffer()); ``` ### Technical Analysis The client uses `VOICEAI_API_BASE` without validating its URL scheme, hostname, port, or origin. All API paths are then constructed from this value, ...[truncated 2420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `VOICEAI_API_BASE` from production builds unless custom endpoints are a documented requirement. 2. For normal production use, hard-code or strictly allowlist the expected origin: ```ts const ALLOWED_API_ORIGINS = new Set(['https://dev.voice.ai']); function validateApiBase(raw: string): string { const url = new URL(raw); if (url.protocol !== 'https:') { throw new Error('VOICEAI_API_BASE must use HTTPS.'); } if (!ALLOWED_API_ORIGINS.has(url.origin)) { throw new Error(`Unapproved Voice.ai API origin: ${url.origin}`); } if (url.username || url.password || url.search || url.hash) { throw new Error('VOICEAI_API_BASE must not contain credentials, query parameters, or fragments.'); } return url.origin; } ``` 3. Never forward a production Voice.ai credential to a non-Voice.ai origin. If development endpoints are required, use a separate development credential variable and require an explicit development flag. 4. Display the effective API origin before any authenticated request when an override is active, and require interactive confirmation where practical. 5. Explicitly load `.env` from a trusted Skill directory rather than the caller’s arbitrary working directory, or document and verify the expected configuration path. 6. Add automated tests proving that HTTP URLs, lookalike domains, embedded credentials, unexpected ports, and unapproved hosts are rejected. 7. Apply outbound network policy or egress allowlisting at deployment level as defense in depth. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/ffmpeg.ts:245
Finding
Command injection through generated Bash and PowerShell muxing scripts<![CDATA[ ## Vulnerability Details **File Location**: `src/ffmpeg.ts:245-270` and `src/ffmpeg.ts:278-300` **Bundled Location**: `voiceai-vo.cjs:4122-4177` **Vulnerability Type**: OS command injection through unsafe script generation **Risk Level**: High ### Vulnerable Code The command string only adds double quotes when an argument contains a space. It does not escape shell or PowerShell metacharacters: ```ts const args = buildMuxArgs({ videoPath, audioPath, outputPath, syncPolicy }); const cmdString = `ffmpeg ${args.map((a) => (a.includes(' ') ? `"${a}"` : a)).join(' ')}`; ``` Attacker-influenced paths are embedded directly into the generated Bash source and executable command: ```ts const bash = `#!/usr/bin/env bash # Replace Audio — generated by voiceai-creator-voiceover-pipeline # Sync policy: ${syncPolicy} # # Prerequisites: Install ffmpeg (https://ffmpeg.org/download.html) # macOS: brew install ffmpeg # Ubuntu: sudo apt install ffmpeg # Windows: choco install ffmpeg OR download from https://ffmpeg.org # # Usage: bash replace-audio.sh set -euo pipefail VIDEO_INPUT="${videoPath}" AUDIO_INPUT="${audioPath}" OUTPUT="${outputPath}" echo "🎬 Muxing audio into video…" echo " Video: \$VIDEO_INPUT" echo " Audio: \$AUDIO_INPUT" echo " Sync: ${syncPolicy}" echo "" ${cmdString} echo "" echo "✅ Done → \$OUTPUT" `; ``` The PowerShell generator escapes backslashes but does not escape quotes, dollar expressions, backticks, newlines, or other PowerShell syntax: ```ts const ps1 = `# Replace Audio — generated by voiceai-creator-voiceover-pipeline # Sync policy: ${syncPolicy} # # Prerequisites: Install ffmpeg (https://ffmpeg.org/download.html) # Windows: choco install ffmpeg OR winget install ffmpeg # # Usage: .\\replace-audio.ps1 $ErrorActionPreference = "Stop" $VideoInput = "${videoPath.replace(/\\/g, '\\\\')}" $AudioInput = "${audioPath.replace(/\\/g, '\\\\')}" $Output = "${outputPath.replace(/\\/g, '\\\\')}" Write-Host "🎬 Muxing au ...[truncated 3247 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer not to generate executable scripts containing interpolated paths. The safest fallback is to print a non-executable explanation and ask the user to rerun the original command after installing ffmpeg. 2. If helper scripts are retained, make them accept file paths as positional parameters rather than embedding CLI values into source code. 3. Use platform-specific, well-tested escaping routines. Bash and PowerShell require different escaping rules; a shared command-string formatter is unsafe. 4. For Bash, represent each static path as a single-quoted literal and replace every embedded single quote using a proven shell-quoting implementation. 5. For PowerShell, use single-quoted literals and escape an embedded single quote by doubling it. Do not place untrusted values in expandable double-quoted strings. 6. Construct the actual invocation from variables or argument arrays rather than interpolating a display-oriented command: ```bash ffmpeg -y -i "$VIDEO_INPUT" -i "$AUDIO_INPUT" ... "$OUTPUT" ``` 7. In PowerShell, invoke ffmpeg with a true argument array: ```powershell $FfmpegArgs = @('-y', '-i', $VideoInput, '-i', $AudioInput, ..., $Output) & ffmpeg @FfmpegArgs ``` 8. Validate paths for control characters and reject NUL bytes, carriage returns, and newlines even after proper quoting. 9. Keep display commands separate from executable script content. A human-readable command formatter must never be reused as a security boundary. 10. Add regression tests using paths containing spaces, quotes, semicolons, dollar signs, `$()`, backticks, ampersands, pipes, redirections, newlines, and PowerShell subexpressions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (62)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a media-processing skill focused on converting scripts into publishable voiceovers using Voice.ai TTS and related publishing features. The supplied code chunk does none of that; it is only an ESLint configuration file for TypeScript projects. Its primary purpose is static analysis/tooling setup, which is materially different and unrelated to the declared functionality. This is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The supplied code does not implement a voiceover publishing pipeline. It is specifically a test suite for chunking-related utilities: parsing markdown headings, splitting text into segments, and generating stable hashes for segments. While segmentation could be a supporting part of the declared system, the code shown lacks any TTS calls, audio generation, captions, chapter publishing, or video muxing. Therefore the actual code chunk’s purpose is materially narrower and different from the declared end-user description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code chunk implements only the preprocessing/chunking portion of a larger possible pipeline. It parses markdown headings, auto-chunks text, loads optional intro/outro templates from disk, and produces structured segment metadata. It does not perform the core declared functions of turning scripts into publishable voiceovers, such as synthesizing audio with Voice.ai, generating captions, or muxing video. While segmentation is consistent with part of the description, the overall declared purpose materially overstates what this code actually does. Additionally, the code reads template files from the filesystem, which is an accessed resource not reflected in the declared permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a broader voiceover-production workflow centered on converting scripts into publishable voiceovers with Voice.ai TTS, plus segments, chapters, captions, and video muxing. The supplied code chunk does not do any TTS, script processing, segmentation, chaptering, or caption work. Its purpose is narrowly limited to replacing/muxing an audio file into an existing video and writing a mux report, with optional helper script generation when ffmpeg is missing. While video muxing is one part of the declared description, the actual code's primary behavior is much narrower and materially different from the declared end-to-end script-to-voiceover functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a content-production workflow centered on turning scripts into publishable voiceovers, including downstream media features like captions and video muxing. This code chunk does not perform any of those actions. Its sole purpose is to fetch and display available voices from Voice.ai. While voice selection could be a supporting part of a broader voiceover tool, this specific code chunk’s primary behavior is materially different from the declared purpose and omits the advertised generation and media-processing capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a media-production skill centered on Voice.ai text-to-speech, segmentation, chapters, captions, and video muxing. The supplied code does not implement or exercise any of those core behaviors. It is only a unit test file for low-level helper utilities such as slugify, hashing, padding, and time formatting. While SRT and YouTube timestamp formatting could be supportive pieces in a captioning pipeline, this chunk by itself is materially different in primary purpose and does not demonstrate the declared voiceover-generation functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a media-processing/TTS skill, but the supplied code only defines test runner configuration for Vitest. It does not implement or expose any Voice.ai integration, voiceover generation, captioning, chaptering, segmentation, or video muxing. This is a clear primary-purpose mismatch.

Chaining Abuse

High
Category
Tool Misuse
Content
brew install ffmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install ffmpeg

# Windows (Chocolatey)
choco install ffmpeg
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**To clear cache manually:**
```bash
rm out/my-project/segments/.cache.json
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { replaceAudioCommand } from './commands/replace-audio.js';
import { voicesCommand } from './commands/voices.js';

// Load .env from project root
config();

const program = new Command();
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
keywords: [
        "dotenv",
        "env",
        ".env",
        "environment",
        "variables",
        "config",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
keywords: [
        "dotenv",
        "env",
        ".env",
        "environment",
        "variables",
        "config",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares access to credentials and clearly describes networked Voice.ai API usage, but it does not declare an explicit tool scope such as allowed tools or permissions. In an agent setting, that omission weakens isolation and user visibility, making it easier for the skill to access environment secrets and external network resources without clear policy boundaries.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill mentions privacy only under the replace-audio section and does not present a prominent warning, before use, that script contents are sent to a third-party TTS provider during normal operation. Users may unknowingly submit sensitive or proprietary scripts, creating confidentiality and compliance risks.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install ffmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install ffmpeg

# Windows (Chocolatey)
choco install ffmpeg
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install ffmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install ffmpeg

# Windows (Chocolatey)
choco install ffmpeg
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
brew install ffmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install ffmpeg

# Windows (Chocolatey)
choco install ffmpeg
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/ffmpeg.ts:49

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
voiceai-vo.cjs:112

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/api.ts:195

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
voiceai-vo.cjs:166