Back to skill

Security audit

Edge Tts

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real text-to-speech skill, but it deserves review because it can send text to an online service, retain generated audio locally, and relies on risky npm dependency patterns.

Install only if you are comfortable sending converted text to Microsoft Edge's online TTS service. Avoid converting secrets, personal data, or proprietary text unless you have permission; prefer explicit TTS commands; clean the temporary audio directory or choose a trusted output path; and update or pin dependencies before relying on it in shared or production environments.

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

Warning
Location
scripts/tts-converter.js:19
Finding
Insecure Shared Temporary Storage for Synthesized Audio<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tts-converter.js:19-20`, `scripts/tts-converter.js:49-60`, and `scripts/tts-converter.js:123-125` **Vulnerability Type**: Unsafe temporary directory and file handling **Risk Level**: Medium ### Vulnerable Code ```javascript const MAX_TEXT_LENGTH = 10000; const TEMP_DIR = path.join(os.tmpdir(), 'edge-tts-temp'); ``` ```javascript async function ensureTempDir() { try { await fs.access(TEMP_DIR); } catch (error) { await fs.mkdir(TEMP_DIR, { recursive: true }); } } /** * Generate unique temporary file path * @param {string} extension - File extension (e.g., '.mp3') * @returns {string} Temporary file path */ function generateTempPath(extension = '.mp3') { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); const filename = `tts_${timestamp}_${random}${extension}`; return path.join(TEMP_DIR, filename); } ``` ```javascript // Ensure temp directory exists and use temp file if no output path specified await ensureTempDir(); const finalOutputPath = outputPath || generateTempPath('.mp3'); ``` The absence of automatic cleanup is also explicitly documented in `SKILL.md:193-195`: ```markdown - **Temporary File Handling**: By default, audio files are saved to the system's temporary directory (`/tmp/edge-tts-temp/` on Unix, `C:\Users\<user>\AppData\Local\Temp\edge-tts-temp\` on Windows) with unique filenames (e.g., `tts_1234567890_abc123.mp3`). Files are not automatically deleted - the calling application (Clawdbot) should handle cleanup after use. ``` ### Technical Analysis The converter places synthesized audio in a fixed directory beneath the system-wide temporary directory. It does not: - Create a process- or user-private temporary directory. - Explicitly enforce owner-only directory permissions such as `0700`. - Verify that an existing directory is owned by the current user. - Verify that the directory or destination is not a symbolic lin ...[truncated 3234 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Create a private temporary directory** - Use `fs.mkdtemp()` beneath `os.tmpdir()` so each execution receives an unpredictable, isolated directory. - Explicitly set or verify directory permissions as `0700`. 2. **Use cryptographically secure names** - Prefer the random directory generated by `mkdtemp()`. - If an additional random filename is required, use `crypto.randomBytes()` or `crypto.randomUUID()` rather than `Math.random()`. 3. **Create files exclusively** - Open output files with exclusive creation semantics, such as `O_CREAT | O_EXCL`. - Enforce mode `0600`. - If the dependency requires a path rather than a file descriptor, validate that the destination does not already exist and use an isolated directory inaccessible to other users. 4. **Validate filesystem objects** - Use `lstat()` rather than only `access()` when validating existing paths. - Reject symbolic links and non-directory objects. - On supported systems, verify that the directory is owned by the effective user and is not writable by group or other users. 5. **Delete temporary artifacts** - Remove generated audio and subtitle files in a `finally` block after they have been delivered or copied to their intended destination. - Remove the private temporary directory recursively after processing. - If deferred delivery prevents immediate deletion, implement a bounded retention period and scheduled application-level cleanup. 6. **Protect caller-selected output paths** - Document that `--output` should point to a trusted directory. - For service integration, restrict output to an approved root and reject path traversal or symbolic-link destinations where user-controlled paths are possible. A safer design would resemble: ```javascript const crypto = require('crypto'); async function createPrivateTempOutput() { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'edge-tts-')); await fs.chmod(tempDir, 0o7 ...[truncated 671 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description says the skill performs text-to-speech conversion and generates audio from text. The supplied code does not synthesize speech, accept text input for conversion, invoke node-edge-tts, or create audio/subtitle output. Instead, it manages persistent user preferences for a separate TTS tool by reading and writing a local JSON config file and exposing command-line options to modify those settings. While the configuration fields align with TTS concepts like voice, language, rate, pitch, and subtitles, the primary purpose of this code chunk is configuration management rather than text-to-speech generation. Therefore, the description materially overstates and misrepresents the actual behavior of this code chunk.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins a transitive dependency on ws 8.19.0, and the supplied advisories indicate that this version is affected by an uninitialized memory disclosure and a memory-exhaustion denial-of-service condition. In a TTS skill that communicates over WebSocket to a remote service, these issues are relevant because malformed or adversarial WebSocket traffic could leak process memory or crash/degrade the service handling requests.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The skill recommends activating on broad user requests for spoken output, which can cause accidental invocation and transmission of content to an external TTS service. In a multi-skill agent, overbroad routing can expose private or sensitive text that the user did not explicitly intend to send for voice synthesis.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill states that it uses Microsoft Edge's online TTS service but does not prominently warn that user text will be transmitted off-box. This creates a privacy risk if users assume processing is local and provide sensitive content, credentials, or proprietary material for speech conversion.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Trigger detection is underspecified and relies on generic keyword matching, making accidental activation likely. Because this skill uses an online Microsoft Edge TTS service, mistaken routing can lead to unintended disclosure of user content to a third party and unnecessary file creation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- Output is MP3 format by default
- Requires internet connection
- Supports subtitle generation (JSON format with word-level timing)
- **Temporary File Handling**: By default, audio files are saved to the system's temporary directory (`/tmp/edge-tts-temp/` on Unix, `C:\Users\<user>\AppData\Local\Temp\edge-tts-temp\` on Windows) with unique filenames (e.g., `tts_1234567890_abc123.mp3`). Files are not automatically deleted - the calling application (Clawdbot) should handle cleanup after use. You can specify a custom output path with the `--output` option if permanent storage is needed.
- **TTS keyword filtering**: The skill automatically filters out TTS-related keywords (tts, TTS, text-to-speech) from text before conversion to avoid converting the trigger words themselves to audio
- For repeated preferences, use `config-manager.js` to set defaults
- **Default voice**: `en-US-MichelleNeural` (female, natural)
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The documentation instructs users to run `npx node-edge-tts` without pinning a version, which causes execution of whatever package version is current at invocation time. If the package is compromised, typosquatted, or a malicious version is later published, users could unintentionally execute attacker-controlled code on their system.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
This example uses `npx node-edge-tts` without a version pin, so it will fetch and execute the latest published package version at runtime. That creates a supply-chain risk where future malicious or compromised releases could be executed directly from the documentation example.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The command example invokes an unpinned `npx` package, which means the code executed is not stable or reproducible and may change over time. In the event of package compromise or malicious publication, users following the documentation could run arbitrary code.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
Running `npx node-edge-tts` without an explicit version delegates trust to the current npm registry state at execution time. That exposes users to package substitution, compromise, or unexpected behavioral changes, all of which are supply-chain hazards.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
This documentation snippet again encourages direct execution of an unversioned npm package via `npx`. Such patterns are risky because they normalize execution of mutable third-party code, enabling arbitrary code execution if the package ecosystem is abused.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The example at this line continues the same unsafe pattern of invoking `node-edge-tts` through unpinned `npx`. Because documentation is often copied verbatim, this creates a realistic pathway for users to execute compromised package code.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The skill hard-codes both the default voice and language to en-US values, which imposes a specific language/locale choice by default. This matches the policy category for language or locale constraints when the user is not offered an explicit opt-in or neutral default.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The test script invokes `npx node-edge-tts` without pinning an exact package version, which can cause execution of whatever version `npx` resolves at runtime. In CI or developer environments, this creates a supply-chain risk because a newer or compromised package release could be fetched and executed unexpectedly.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script sends arbitrary input text to Microsoft Edge's online TTS service, but the interface and help text do not clearly warn users that their content leaves the local environment. In a skill context, users may provide sensitive prompts, credentials, personal data, or proprietary text assuming local processing, creating a privacy and data-handling risk.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The header comment says the tool supports multiple output formats, but when no explicit output path is provided the code unconditionally generates a temporary filename with a .mp3 extension. That means the advertised format flexibility is not consistently reflected in actual file handling, creating a documentation-to-code contradiction about outputs.

Vague Triggers

Low
Confidence
84% confidence
Finding
Using the standalone keyword 'tts' without scope constraints is a weak activation mechanism that can be triggered in unrelated contexts, including quoted text or discussions about TTS itself. The main risk is unintended external processing of text and spurious audio generation rather than direct code execution.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The guide describes use of an online TTS service and file output generation, but the examples do not prominently warn that input text is transmitted to an external service and that audio/subtitle files are written locally. In an agent skill context, this can lead to inadvertent disclosure of sensitive content or unexpected artifact creation if operators assume processing is local and ephemeral.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"test": "npx node-edge-tts -t \"Hello, world! This is a test of the Edge TTS service.\" -f test-output.mp3 && echo \"Test completed successfully!\""
  },
  "dependencies": {
    "node-edge-tts": "^1.2.9",
    "commander": "^11.0.0"
  },
  "author": "Clawdbot",
Confidence
89% confidence
Finding
Using the caret range `^1.2.9` for `node-edge-tts` allows future minor and patch releases to be installed automatically, which reduces build reproducibility and can introduce unreviewed upstream changes. Because this package is directly related to the skill's core TTS functionality and may be executed in tooling or tests, compromise of the dependency could affect the environment running the skill.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "node-edge-tts": "^1.2.9",
    "commander": "^11.0.0"
  },
  "author": "Clawdbot",
  "license": "MIT"
Confidence
87% confidence
Finding
Using the caret range `^11.0.0` for `commander` permits automatic installation of later compatible releases, introducing supply-chain and reproducibility risk. Although `commander` is a common CLI library and lower risk than executing remote commands directly, unpinned dependency drift can still bring vulnerable or malicious code into the project.

Intent-Code Divergence

Low
Confidence
86% confidence
Finding
The function documentation describes converting the input text to speech, yet the implementation filters out words like "tts" and "text-to-speech" before sending content to the synthesizer. This is a behavioral contradiction because the documented intent is to synthesize the provided text, not a modified version of it.

Static analysis

No suspicious patterns detected.