Back to skill

Security audit

Xiaozhi Mcp Music Official

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent XiaoZhi music bridge, but it exposes overbroad local process control through remote MCP-invoked tools.

Review before installing. Use only with a trusted XiaoZhi MCP endpoint and a trusted, fixed PLAYER_CMD. Be aware that music searches and the API key are sent to third-party music APIs, and that the current stop/pause/resume implementation can terminate or suspend unrelated processes matching the configured player command. A safer version should track and signal only the player process it starts and pin reviewed dependency versions.

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

Warning
Location
music_mcp.py:117
Finding

Broad Process Control Through an Unscoped Command-Line Pattern

Content
View full analysis
str: try: subprocess.run(['pkill', '-f', PLAYER_CMD], check=False) return '已经停止播放。' except Exception as e: return f'停止失败:{str(e)}' @mcp.tool() def pause_music() -> str: try: subprocess.run(['pkill', '-STOP', '-f', PLAYER_CMD], check=False) return '音乐已暂停。' except Exception as e: return f'暂停失败:{str(e)}' @mcp.tool() def resume_music() -> str: try: subprocess.run(['pkill', '-CONT', '-f', PLAYER_CMD], check=False) return '继续播放。' except Exception as e: return f'继续播放失败:{str(e)}' ``` ### Technical Analysis The three remotely exposed MCP tools use `pkill -f` with the configured `PLAYER_CMD` as a process-matching pattern. The `-f` option matches against complete process command lines and is not restricted to player processes started by this application. Consequently, every accessible process whose command line matches the configured string may be terminated, suspended, or resumed. The code does not retain the `subprocess.Popen` object or PID created by `play_url`, verify process ownership beyond the operating system's ordinary user boundary, or confirm that a matched process is one of the application's players. Although the list-form subprocess invocation prevents ordinary shell metacharacter injection, it does not prevent overly broad process matching. The effect is particularly dangerous if `PLAYER_CMD` is short, generic, or incorrectly configured. ### Attack Path 1. The service starts with `PLAYER_CMD` set to a value that also appears in unrelated process command lines. 2. An attacker or unauthorized caller with access to the connected MCP endpoint invokes `stop_music`, `pause_music`, or `resume_music`. 3. The application executes `pkill -f` wit ...[truncated 965 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
music_mcp.py:24
Finding

Music API Credential Exposed in Request URLs

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (29)

Tainted flow: 'params' from os.environ.get (line 26, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · music_mcp.py (reported line 32)May include surrounding context.

python
'n': n,
        'type': quality,
    }
    r = requests.get(url, params=params, timeout=20)
    r.raise_for_status()
    return r.json()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 48)May include surrounding context.

启动

bash
pip install -r requirements.txt
cp .env.example .env
python3 mcp_pipe.py music_mcp.py

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

启动

bash
pip install -r requirements.txt
cp .env.example .env
python3 mcp_pipe.py music_mcp.py

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The markdown states that the skill will call a local player directly when it obtains a direct link, which implies local process execution. The README does not include any warning that this may launch local software or execute the configured PLAYER_CMD, which could affect the user's system state.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises capabilities that involve environment variables, outbound network access, and invocation of a local player command, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, missing scope metadata can cause users or orchestrators to under-estimate what the skill can access and execute, increasing the chance of over-privileged or unsafe deployment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The description says the skill connects to a XiaoZhi MCP endpoint and online music APIs, but it does not clearly warn that user search queries, playback requests, and related metadata are transmitted to external services. This creates a privacy and data-handling risk because users may unknowingly expose listening intent, prompts, or identifiers to third parties.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill states it uses mpv to play network audio URLs but does not explicitly warn that it will invoke a local media player command on the host to open remote content. Launching a local player against externally supplied URLs increases host exposure to unsafe content handling, unexpected process execution, and confusion about what runs locally versus remotely.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This bridge will execute whatever local Python script is supplied on the command line, while also wiring it directly to the configured MCP WebSocket endpoint. In the context of an MCP integration, this creates a powerful execution-and-bridging primitive that can be abused to run unintended local code and expose it to remote input/output, expanding the trust boundary far beyond a music skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

All data received from the remote WebSocket is forwarded directly into the stdin of the spawned local process, and that process's stdout is sent back over the network without validation or user disclosure. In this skill context, the bridge is intended behavior, but it still creates a risky opaque trust channel where a remote endpoint can drive local tool behavior and potentially trigger unsafe actions in whatever script was launched.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · mcp_pipe.py (reported line 51)May include surrounding context.

python
script_path = sys.argv[1]
    async with websockets.connect(uri) as websocket:
        process = subprocess.Popen(
            ['python3', script_path],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill sends user search queries and an API key to third-party music APIs without explicit disclosure. This is primarily a privacy and transparency issue rather than code execution, but in a voice/assistant context users may not expect their requests to be forwarded externally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The tool can cause immediate local side effects by launching a media player and making the host fetch remote content, but the function description does not meaningfully warn users about this local execution behavior. In assistant-integrated environments, insufficient disclosure can lead to surprising or unsafe host actions from a simple natural-language request.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
91% confidence
Finding

The tool launches a local executable using a configurable environment variable and a URL obtained from an untrusted remote API. Although shell metacharacter injection is avoided by passing an argument list, this still permits execution of an unexpected binary if PLAYER_CMD is misconfigured or attacker-controlled, and it causes the host to fetch/play arbitrary remote content.

Content

Scanner excerpt · music_mcp.py (reported line 58)May include surrounding context.

python
def play_url(url: str):
    # 直接交给本地播放器。mpv 对网络资源支持最好。
    subprocess.Popen([PLAYER_CMD, url])


@mcp.tool()

Tainted flow: 'PLAYER_CMD' from os.environ.get (line 11, credential/environment) → subprocess.Popen (code execution)

Medium
Category
Data Flow
Confidence
90% confidence
Finding

PLAYER_CMD comes from the environment and is executed as a program, so a compromised deployment or untrusted configuration can cause arbitrary executable launch. The code does not invoke a shell, which reduces classic command injection risk, but execution of an attacker-chosen binary is still possible.

Content

Scanner excerpt · music_mcp.py (reported line 58)May include surrounding context.

python
def play_url(url: str):
    # 直接交给本地播放器。mpv 对网络资源支持最好。
    subprocess.Popen([PLAYER_CMD, url])


@mcp.tool()

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill exposes stop, pause, and resume by sending host OS signals to processes selected by a configurable name, not to a dedicated subprocess it owns. In an MCP skill, where tool invocation can be indirectly driven by user prompts, this significantly increases danger because normal music commands can affect unrelated local processes.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
94% confidence
Finding

This code sends pkill -f against a configurable pattern, which can match arbitrary local processes whose command line contains PLAYER_CMD. If PLAYER_CMD is broad or maliciously set, stop_music can terminate unrelated processes on the host, making the skill capable of OS-level denial of service beyond music playback.

Content

Scanner excerpt · music_mcp.py (reported line 122)May include surrounding context.

python
@mcp.tool()
def stop_music() -> str:
    try:
        subprocess.run(['pkill', '-f', PLAYER_CMD], check=False)
        return '已经停止播放。'
    except Exception as e:
        return f'停止失败:{str(e)}'

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

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The configurable PLAYER_CMD value flows into pkill -f, where it is interpreted as a process-match pattern. This lets configuration control which local processes are terminated, potentially far beyond the intended player process, creating a host-level denial-of-service primitive.

Content

Scanner excerpt · music_mcp.py (reported line 122)May include surrounding context.

python
@mcp.tool()
def stop_music() -> str:
    try:
        subprocess.run(['pkill', '-f', PLAYER_CMD], check=False)
        return '已经停止播放。'
    except Exception as e:
        return f'停止失败:{str(e)}'

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
94% confidence
Finding

pause_music uses pkill -STOP -f with a configurable pattern, allowing the skill to suspend arbitrary matching processes rather than only the music player it started. In the MCP context this gives a remote-triggerable mechanism to freeze host processes, which is more dangerous than normal media control.

Content

Scanner excerpt · music_mcp.py (reported line 131)May include surrounding context.

python
@mcp.tool()
def pause_music() -> str:
    try:
        subprocess.run(['pkill', '-STOP', '-f', PLAYER_CMD], check=False)
        return '音乐已暂停。'
    except Exception as e:
        return f'暂停失败:{str(e)}'

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

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The environment-derived PLAYER_CMD controls which processes receive SIGSTOP via pkill -f. In practice, a bad value can pause unrelated system or user processes, and because the action is exposed as a tool it becomes remotely triggerable through the assistant workflow.

Content

Scanner excerpt · music_mcp.py (reported line 131)May include surrounding context.

python
@mcp.tool()
def pause_music() -> str:
    try:
        subprocess.run(['pkill', '-STOP', '-f', PLAYER_CMD], check=False)
        return '音乐已暂停。'
    except Exception as e:
        return f'暂停失败:{str(e)}'

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
93% confidence
Finding

resume_music issues pkill -CONT -f against a configurable pattern, which can resume unrelated suspended processes. While less destructive than STOP or kill, it still gives the skill OS-level control over arbitrary local processes matching PLAYER_CMD.

Content

Scanner excerpt · music_mcp.py (reported line 140)May include surrounding context.

python
@mcp.tool()
def resume_music() -> str:
    try:
        subprocess.run(['pkill', '-CONT', '-f', PLAYER_CMD], check=False)
        return '继续播放。'
    except Exception as e:
        return f'继续播放失败:{str(e)}'

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

Medium
Category
Data Flow
Confidence
94% confidence
Finding

The environment-derived process-match pattern also controls which processes receive SIGCONT. This grants broader-than-necessary OS process control and can interfere with unrelated applications, even though the direct impact is generally lower than kill or stop.

Content

Scanner excerpt · music_mcp.py (reported line 140)May include surrounding context.

python
@mcp.tool()
def resume_music() -> str:
    try:
        subprocess.run(['pkill', '-CONT', '-f', PLAYER_CMD], check=False)
        return '继续播放。'
    except Exception as e:
        return f'继续播放失败:{str(e)}'

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The documentation instructs users to place MUSIC_API_KEY in a .env file but provides no warning about keeping credentials secret or avoiding accidental disclosure. For markdown files, omission of warnings around handling sensitive credentials is a relevant safety concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The skill is presented with a hard-coded Chinese name, and user-facing strings throughout the file are also fixed in Chinese. This can be a language/locale policy issue when the skill forces a specific language without any opt-in or documented regional scope.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with only a lower bound, which makes builds non-reproducible and allows future installs to pull in unexpected versions, including vulnerable or breaking releases. In a skill that bridges network services and local media playback, supply-chain drift increases risk because behavior may change without review.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
fastmcp>=0.1.0
websockets>=12.0
python-dotenv>=1.0.0

Unverifiable Dependency: fastmcp has 14 known advisory(ies) (CVE-2025-69196 (FastMCP OAuth Proxy token reuse across MCP servers); GHSA-c2jp-c369-7pvx (FastMCP Auth Integration Allows for Confused Deputy Account Takeover); CVE-2025-64340 (FastMCP has a Command Injection vulnerability - Gemini CLI) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

The manifest does not pin fastmcp, and the package has multiple known advisories, so there is no way to verify whether the installed version is affected. This is more concerning in this skill because FastMCP is core to the MCP bridge and may process authentication, tool exposure, and server interactions, amplifying the effect of any upstream flaw.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.