Back to skill

Security audit

speechfy

Security checks across malware telemetry and agentic risk

Overview

This is a coherent text-to-speech skill that uses disclosed external TTS providers and local audio conversion, with some normal privacy and overwrite cautions for users.

Install only if you are comfortable sending the text you synthesize to Speechify or Microsoft Edge TTS. Do not use it for secrets, regulated data, or private messages without approval, and choose output paths deliberately because existing writable files can be overwritten.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (8)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
vault_resolver = os.environ.get("VAULT_RESOLVER", "vault-resolver")
    if os.path.isfile(vault_resolver):
        try:
            proc = subprocess.run(
                [vault_resolver, "resolve"],
                input=json.dumps({"ids": [VAULT_ITEM]}).encode(),
                capture_output=True, timeout=20
Confidence
92% confidence
Finding
The script executes a helper binary whose path is taken from the VAULT_RESOLVER environment variable, with only an os.path.isfile check before execution. In an agent or automation context, an attacker who can influence environment variables or place a file at that path can cause arbitrary code execution under the skill's privileges.

Tainted flow: 'vault_resolver' from os.environ.get (line 39, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
vault_resolver = os.environ.get("VAULT_RESOLVER", "vault-resolver")
    if os.path.isfile(vault_resolver):
        try:
            proc = subprocess.run(
                [vault_resolver, "resolve"],
                input=json.dumps({"ids": [VAULT_ITEM]}).encode(),
                capture_output=True, timeout=20
Confidence
96% confidence
Finding
The path stored in VAULT_RESOLVER is read from the environment and executed as a program, which is a classic unsafe trust boundary. Because the code only checks whether the file exists, a malicious or replaced helper can exfiltrate secrets or run arbitrary actions before returning fake vault data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill metadata declares required binaries and clearly documents use of environment variables, networked third-party APIs, and shell execution, but it does not declare corresponding permissions/capabilities. This creates a transparency and governance gap: operators may approve or run the skill without understanding that user text and secrets may be accessed and transmitted externally.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README explicitly describes sending user-provided text to external TTS providers but does not warn that prompts, messages, or other potentially sensitive content will leave the local environment and be processed by third parties. In an agent skill context, users may unknowingly synthesize confidential data, making the omission a real privacy/security issue even though it is documentation-related rather than a code execution flaw.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The description presents the skill as a local TTS utility but omits an upfront warning that input text is sent to Speechify and, on fallback, to Edge TTS. This is risky because users may provide sensitive or regulated content under the assumption it stays local, causing unintended third-party disclosure.

Unvalidated Output Injection

High
Category
Output Handling
Content
mp3_tmp = output_path + ".tmp.mp3"
        with open(mp3_tmp, "wb") as f:
            f.write(base64.b64decode(audio_b64))
        subprocess.run(
            ["ffmpeg", "-y", "-i", mp3_tmp,
             "-c:a", "libopus", "-b:a", "32k",
             output_path],
Confidence
88% confidence
Finding
output_path comes from a CLI argument and is passed to ffmpeg and prior file writes without validation or sandboxing. In an agent environment, this allows arbitrary file creation or overwrite anywhere the process has access, potentially clobbering application files, user data, or sensitive paths under elevated privileges.

Unvalidated Output Injection

High
Category
Output Handling
Content
edge_cmd = resolve_edge_cmd()
    tmp = output_path + ".edge.mp3"
    try:
        subprocess.run(
            [edge_cmd, "--voice", EDGE_VOICE,
             "--text", text,
             "--write-media", tmp],
Confidence
82% confidence
Finding
The temporary filename tmp is derived directly from unvalidated output_path, so edge-tts can be instructed to write attacker-chosen files or follow dangerous paths. This broadens the arbitrary file-write surface beyond the final ffmpeg output to an additional intermediate artifact.

Unvalidated Output Injection

High
Category
Output Handling
Content
)
        # edge-tts grava MP3 mesmo com extensão .ogg — converte para Opus
        # para garantir voice bubble nativa no Telegram.
        subprocess.run(
            ["ffmpeg", "-y", "-i", tmp,
             "-c:a", "libopus", "-b:a", "32k",
             output_path],
Confidence
88% confidence
Finding
ffmpeg writes to output_path with -y, forcibly overwriting existing files if permitted. Because the path is user-controlled and unchecked, a caller can replace arbitrary writable files, and the TTS/agent context makes this more dangerous if the skill runs with broad filesystem permissions.

VirusTotal

65/65 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.