Back to skill

Security audit

Ms Speech Synth

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent text-to-speech tool, but it needs Review because it can run an unverified ffmpeg binary from a predictable shared temporary path and sends input text to Microsoft Edge TTS.

Review before installing. Use a trusted system ffmpeg or pass an explicit trusted --ffmpeg path, do not follow the /tmp curl download guidance, and avoid sending confidential or regulated text because synthesis uses Microsoft Edge TTS. Run it only in an environment where temporary audio files and user-level subprocess execution are acceptable.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (3)

T08 · Insecure Dependencies

Warning
Location
SKILL.md:37
Finding
Unverified FFmpeg Binary Download from a Third-Party Distribution Source<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:37`; `scripts/ms_tts_chunked_bgm.py:390-395` **Vulnerability Type**: Unverified third-party executable dependency **Risk Level**: Medium ### Vulnerable Code From `SKILL.md:37`: ```markdown | `ffmpeg` | `curl -L .../ffmpeg.zip -o /tmp/ffmpeg.zip && unzip /tmp/ffmpeg.zip -d /tmp/ffmpeg_bin` | WAV 转 MP3、BGM 混音 | ``` From `scripts/ms_tts_chunked_bgm.py:390-395`: ```python if ffmpeg_path is None: print('[MS-TTS-BGM] Warning: ffmpeg not found, outputting WAV instead.') print('[MS-TTS-BGM] Install ffmpeg: curl -L https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip -o /tmp/ffmpeg.zip && unzip /tmp/ffmpeg.zip -d /tmp/ffmpeg_bin') if bgm_path: raise RuntimeError('ffmpeg is required when --bgm is provided.') ``` ### Technical Analysis The Skill recommends downloading a precompiled FFmpeg archive from `evermeet.cx`, which is a third-party distribution source rather than an official FFmpeg release channel or a trusted operating-system package repository. The command uses a moving release endpoint and does not: - Pin an exact FFmpeg version. - Verify a cryptographic checksum. - Verify a release signature. - Validate the archive contents before extraction. - Validate the ownership or integrity of the resulting executable. Consequently, the binary eventually executed by the Skill can differ from the artifact originally reviewed. A compromise of the distribution service, its release pipeline, or the downloaded archive could introduce an attacker-controlled executable. The command is presented as installation guidance rather than being executed automatically by the Python script. Exploitation therefore requires a user or agent to follow the displayed installation command. ### Attack Path 1. The expected FFmpeg executable is unavailable on the host. 2. The Skill prints or presents the recommended `curl` and `unzip` installation command. 3. A user or agent follows that guidance. 4. The th ...[truncated 935 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the third-party moving download endpoint with an operating-system package manager or another explicitly trusted distribution channel. 2. If a standalone binary is necessary, pin an exact release version and expected platform. 3. Publish and verify a trusted SHA-256 or stronger digest before extracting or executing the archive. 4. Verify upstream release signatures where available. 5. Extract into a newly created, user-owned directory rather than a predictable shared `/tmp` path. 6. Inspect the archive for absolute paths, parent-directory traversal, links, and unexpected files before extraction. 7. Store the verified executable in a non-shared application or user data directory with restrictive permissions. 8. Document the external source, pinned version, expected digest, and trust assumptions in `SKILL.md`. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/ms_tts_chunked_bgm.py:87
Finding
Automatic Execution of a Predictable FFmpeg Binary from Shared Temporary Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ms_tts_chunked_bgm.py:87-97`, with execution at `scripts/ms_tts_chunked_bgm.py:346-364` and `420-425` **Vulnerability Type**: Local executable search-path hijacking **Risk Level**: High ### Vulnerable Code Executable discovery at `scripts/ms_tts_chunked_bgm.py:87-97`: ```python def find_ffmpeg() -> str | None: paths = [ '/tmp/ffmpeg_bin/ffmpeg', '/usr/local/bin/ffmpeg', '/opt/homebrew/bin/ffmpeg', '/usr/bin/ffmpeg', ] for p in paths: if os.path.isfile(p) and os.access(p, os.X_OK): return p return None ``` Execution during BGM mixing at `scripts/ms_tts_chunked_bgm.py:346-364`: ```python command = [ ffmpeg_path, '-y', '-i', voice_wav_path, *bgm_input_args, '-i', bgm_path, '-filter_complex', filter_complex, '-map', '[mix]', '-b:a', '192k', output_mp3_path, ] completed = subprocess.run(command, capture_output=True, text=True) ``` Execution during ordinary conversion at `scripts/ms_tts_chunked_bgm.py:420-425`: ```python if ffmpeg_path and os.path.exists(ffmpeg_path): subprocess.run([ ffmpeg_path, '-y', '-i', wav_path, '-b:a', '192k', mp3_path ], capture_output=True) ``` ### Technical Analysis The executable discovery routine prioritizes `/tmp/ffmpeg_bin/ffmpeg` over system installation paths. `/tmp` is normally shared among local users and processes, and `/tmp/ffmpeg_bin/ffmpeg` is a fixed, predictable location. The validation consists only of checking that the path is a regular file and executable. It does not verify: - Ownership of the file or parent directory. - Whether another user can modify the file or directory. - A trusted cryptographic digest. - Whether the path or any parent component involves a symbolic link. - Whether the file is the expected FFmpeg executable. Although `subprocess.run()` uses an argument list and is not vulnerable to shell metacharacter ...[truncated 1607 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `/tmp/ffmpeg_bin/ffmpeg` from automatic executable discovery. 2. Prefer trusted system package locations or require an explicit absolute path supplied through configuration. 3. If a bundled executable is supported, store it in an application-owned directory that is not writable by other users. 4. Before execution, validate that the executable and every parent directory: - Are owned by the expected user or administrator. - Are not writable by unauthorized users. - Are not symbolic links. 5. Verify the executable against a pinned cryptographic digest or trusted signature. 6. Open or resolve the executable safely and mitigate check-to-use races where the platform permits. 7. Run FFmpeg in a restricted subprocess environment with minimum filesystem and network access. 8. Log the resolved executable path and validation result before execution. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ms_tts_chunked_bgm.py:170
Finding
Predictable Intermediate Audio Files Created in Shared Temporary Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ms_tts_chunked_bgm.py:170-184` **Vulnerability Type**: Insecure predictable temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```python for i, chunk in enumerate(chunks): mp3_file = f"/tmp/ms_tts_chunk_{os.getpid()}_{i}.mp3" is_last = i == len(chunks) - 1 seg_delay = 0.0 if is_last else rate_limit_delay await generate_segment(chunk, voice, mp3_file, seg_delay, speed) with open(mp3_file, 'rb') as f: decoded = miniaudio.decode(f.read()) sample_rate = decoded.sample_rate nchannels = decoded.nchannels if hasattr(decoded.samples, 'tobytes'): all_pcm += decoded.samples.tobytes() else: all_pcm += bytes(decoded.samples) os.unlink(mp3_file) ``` ### Technical Analysis Intermediate MP3 files are created directly under `/tmp` using a filename composed only of the process ID and sequential chunk index. These values are predictable or observable by other local processes. The implementation does not use `tempfile`, exclusive file creation, a private temporary directory, symlink rejection, or restrictive directory permissions. The generated path is passed to `edge_tts.Communicate.save()`, then reopened and deleted by pathname. This creates a possible race between path selection, creation, reading, and deletion. Depending on how the dependency opens the destination file and the operating system's symlink protections, another local user may be able to pre-create or replace the path with a symlink or attacker-controlled file. The exact overwrite behavior depends on `edge_tts` and host-level protections, so arbitrary overwrite is a potential rather than guaranteed outcome. Predictable path collision and denial of service remain possible where another process can manipulate the path. ### Attack Path 1. A local attacker observes or predicts the victim process ID. 2. The attacker predicts a chunk filename such as `/tmp/ms_tts_chunk ...[truncated 1360 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private temporary directory with `tempfile.TemporaryDirectory()` for each synthesis operation. 2. Store every intermediate segment inside that private directory. 3. Ensure the directory is created with permissions restricting access to the current user. 4. Use securely generated random filenames rather than process IDs and counters alone. 5. Where a dependency requires a pathname, ensure the parent directory is private and reject symbolic links. 6. Perform cleanup through context managers or `try`/`finally` so temporary audio is removed after both success and failure. 7. Consider validating file type and size before decoding downloaded segment data. 8. Avoid loading all intermediate PCM data into one unbounded in-memory byte string; stream it to the output WAV file to reduce denial-of-service risk for very large inputs. A safer structure would be: ```python import tempfile from pathlib import Path with tempfile.TemporaryDirectory(prefix="ms_tts_") as temp_dir: mp3_file = Path(temp_dir) / f"chunk_{i}.mp3" await generate_segment(chunk, voice, str(mp3_file), seg_delay, speed) with mp3_file.open("rb") as f: decoded = miniaudio.decode(f.read()) ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill documentation instructs use of file access and shell execution but does not declare any explicit tool scope such as permissions or allowed-tools. That creates an overbroad execution model where an agent may invoke filesystem and shell capabilities without clear limitation, increasing the risk of unintended command execution or access to sensitive files when the skill is used.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill sends user-provided text to Microsoft's Edge TTS service over the network, but the documentation does not clearly disclose this data transfer. Users may provide sensitive notes, documents, or markdown content under the assumption processing is local, causing unintended exposure of private or regulated data to a third-party service.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
The natural-language description states the skill converts arbitrary-length Chinese text and the documented defaults and examples are centered on Chinese voices and Chinese-only processing. This creates a locale-specific constraint without an explicit opt-in or justification that the skill is intentionally region/language scoped.

External Transmission

Medium
Category
Data Exfiltration
Content
|------|------|------|
| `edge-tts` | `pip3 install edge-tts` | 微软 TTS 引擎 |
| `miniaudio` | `pip3 install miniaudio` | MP3 解码为 PCM |
| `ffmpeg` | `curl -L .../ffmpeg.zip -o /tmp/ffmpeg.zip && unzip /tmp/ffmpeg.zip -d /tmp/ffmpeg_bin` | WAV 转 MP3、BGM 混音 |

## 速率限制(Rate Limit)
Confidence
91% confidence
Finding
The documentation recommends downloading and unpacking ffmpeg via curl from an unspecified external URL directly into a temporary directory. This encourages execution of externally fetched binaries without integrity verification or pinned source validation, which can lead to supply-chain compromise or arbitrary code execution if the download source is tampered with or replaced.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill sends supplied text to Microsoft's Edge TTS service via edge_tts.Communicate without any clear user-facing disclosure that content leaves the local environment. If users process sensitive notes, documents, or folder contents, this can cause unintended data exposure to a third-party service and violate privacy or compliance expectations.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
output_mp3_path,
    ]

    completed = subprocess.run(command, capture_output=True, text=True)
    if completed.returncode != 0:
        stderr = (completed.stderr or '').strip()
        raise RuntimeError(f'ffmpeg bgm mix failed: {stderr[:400]}')
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

External Transmission

Medium
Category
Data Exfiltration
Content
if ffmpeg_path is None:
        print('[MS-TTS-BGM] Warning: ffmpeg not found, outputting WAV instead.')
        print('[MS-TTS-BGM] Install ffmpeg: curl -L https://evermeet.cx/ffmpeg/getrelease/ffmpeg/zip -o /tmp/ffmpeg.zip && unzip /tmp/ffmpeg.zip -d /tmp/ffmpeg_bin')
        if bgm_path:
            raise RuntimeError('ffmpeg is required when --bgm is provided.')
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return wav_result

    if ffmpeg_path and os.path.exists(ffmpeg_path):
        subprocess.run([
            ffmpeg_path, '-y', '-i', wav_path,
            '-b:a', '192k', mp3_path
        ], capture_output=True)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.