Back to skill

Security audit

xeon_tts

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real local TTS and voice-cloning skill, but it exposes powerful unauthenticated services and enables persistent background services in ways users should review carefully.

Review before installing. Run it only on a trusted local machine or firewall port 9002, avoid exposing the Node gateway to a network, and consider changing the service to bind to 127.0.0.1. Do not use the direct clone-speak endpoint with arbitrary file paths, and prefer disabling autostart until you have reviewed the systemd units and dependency sources.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.js:676
Finding

<![CDATA[Unauthenticated TTS Gateway Exposed on All Network Interfaces]]>

Content
View full analysis
{ try { if (req.method === 'GET' && req.url === '/health') { return sendJson(res, 200, { status: 'ok', port: config.port }); } if (req.method === 'GET' && req.url.startsWith('/api/session/state')) { const url = new URL(req.url, 'http://127.0.0.1'); const sessionId = url.searchParams.get('sessionId') || config.openclawSession; const userId = url.searchParams.get('userId') || 'anonymous'; const current = getSessionState(sessionId, userId).session; return sendJson(res, 200, { success: true, session: current }); } if (req.method === 'POST' && req.url === '/api/workflow/message') { const body = await readJsonBody(req); const result = await handleWorkflowMessage(body || {}); return sendJson(res, 200, result); } if (req.method === 'POST' && req.url === '/api/tts/custom-speak') { const body = await readJsonBody(req); const result = await synthesizeCustomVoice({ text: body.text, style: body.style || '普通', language: body.language || config.defaultLanguage, speakerId: body.speakerId || config.defaultSpeaker, }); return sendJson(res, 200, { success: true, ...result }); } if (req.method === 'POST' && req.url === '/api/tts/clone-speak') { const body = await readJsonBody(req); const result = await synthesizeClonedVoice({ text: body.text, referenceAudioPath: body.referenceAudioPath, language: body.language || config.defaultLanguage, referenceText: body.referenceText || '', }); return sendJson(res, 200, { success: true, ...result }); } return sendJson(res, 404, { success: false, error: 'not found' }) ...[truncated 2489 chars]
Remediation
View remediation
{ // ... }); ``` 2. If remote access is required, require an unpredictable bearer token or mutually authenticated TLS before processing any `/api/*` route. 3. Authorize session access using trusted identity supplied by the authenticated OpenClaw integration; do not treat caller-selected `userId` or `sessionId` values as proof of identity. 4. Restrict port 9002 using host firewall rules and network access-control lists. 5. Put remote deployments behind a TLS-enabled reverse proxy. 6. Add request-rate, inference-concurrency, and per-user quota controls. 7. Return only necessary session fields and avoid disclosing absolute filesystem paths. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
server.js:527
Finding

<![CDATA[Arbitrary Local File Read Through referenceAudioPath]]>

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:265
Finding

<![CDATA[Unbounded Request Bodies and Insufficient Resource-Abuse Controls]]>

Content
View full analysis
{ const chunks = []; req.on('data', (chunk) => chunks.push(chunk)); req.on('end', () => { try { const text = Buffer.concat(chunks).toString('utf8'); resolve(text ? JSON.parse(text) : {}); } catch (error) { reject(error); } }); req.on('error', reject); }); } ``` ```js function parseMultipart(req) { const form = new formidable.IncomingForm({ uploadDir: config.runtimeDir, keepExtensions: true, multiples: false, }); return new Promise((resolve, reject) => { form.parse(req, (error, fields, files) => { if (error) { reject(error); return; } resolve({ fields, files }); }); }); } ``` ### Technical Analysis JSON requests are accumulated into an unrestricted array and concatenated only after the client finishes sending data. There is no byte counter, `Content-Length` validation, request timeout, or early termination after a configured threshold. The multipart parser is constructed without an explicit conservative `maxFileSize`, `maxTotalFileSize`, or field-size policy appropriate for the expected three-to-five-second reference clips. Uploaded data is written into `config.runtimeDir`. The application also lacks authentication, rate limiting, per-client quotas, and inference-concurrency controls. Text-duration validation limits estimated audio duration, but it does not prevent attackers from repeatedly submitting valid-size requests or oversized transport bodies. ### Attack Path 1. An attacker connects to port 9002. 2. For memory exhaustion, the attacker sends a very large or indefinitely streamed JSON body to a JSON endpoint. 3. The server retains each rec ...[truncated 903 chars]
Remediation
View remediation
{ received += chunk.length; if (received > MAX_JSON_BYTES) { req.destroy(new Error('Request body too large')); return; } chunks.push(chunk); }); ``` 2. Configure Formidable with limits suitable for short audio clips: ```js const form = new formidable.IncomingForm({ uploadDir: config.runtimeDir, keepExtensions: true, multiples: false, maxFiles: 1, maxFileSize: 10 * 1024 * 1024, maxTotalFileSize: 10 * 1024 * 1024, maxFields: 10, maxFieldsSize: 64 * 1024, }); ``` 3. Set HTTP header, request, and idle timeouts. 4. Authenticate callers and enforce per-user and per-IP rate limits. 5. Limit concurrent inference jobs and queue depth. 6. Check available disk space and apply per-user storage quotas. 7. Ensure temporary files are removed in `finally` blocks on every success and failure path. 8. Apply systemd memory, CPU, process, and filesystem quotas as defense in depth. ]]>

T08 · Insecure Dependencies

Warning
Location
setup_env.sh:110
Finding

<![CDATA[Executable Dependencies and Installer Downloaded Without Immutable Pinning or Integrity Verification]]>

Content
View full analysis
/dev/null 2>&1 rm -f /tmp/miniconda.sh fi PYTHON_CMD="$conda_dir/bin/python" [[ -x "$PYTHON_CMD" ]] || { log_error "Miniconda 安装失败"; exit 1; } log_info "Python 就绪: $($PYTHON_CMD --version 2>&1)" } ``` Python dependencies use mutable, unpinned package specifications: ```bash setup_venv() { if [[ "$FORCE" -eq 1 && -d venv ]]; then rm -rf venv fi if [[ ! -d venv ]]; then log_step "创建虚拟环境" "$PYTHON_CMD" -m venv venv fi source venv/bin/activate pip install -q --upgrade pip } install_python_packages() { log_step "安装 Python TTS 服务包" if ! pip install -q --upgrade "$TTS_PIP_SPEC"; then log_error "安装失败: $TTS_PIP_SPEC" log_error "如果包尚未发布,请设置 XDP_TTS_PIP_SPEC=/path/to/xdp_tts_service.whl 后重试" exit 1 fi log_info "已安装: $TTS_PIP_SPEC" } ``` The default package specification is not version-pinned: ```bash TTS_PIP_SPEC="${XDP_TTS_PIP_SPEC:-xdp-tts-service}" ``` The Hugging Face client and model repositories are likewise not pinned to immutable versions or revisions: ```bash if pip install -q 'huggingface_hub[cli]' >/dev/null 2>&1 || pip install -q huggingface_hub >/dev/null 2>&1; then ``` ```bash if [[ "$(basename "$hf ...[truncated 2736 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (42)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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).

Content

Scanner excerpt · setup_env.sh (reported line 117)May include surrounding context.

sh
if [[ ! -d "$conda_dir" ]]; then
    wget --timeout=120 -q "$conda_url" -O /tmp/miniconda.sh || curl -fsSL --connect-timeout 120 "$conda_url" -o /tmp/miniconda.sh
    bash /tmp/miniconda.sh -b -p "$conda_dir" >/dev/null 2>&1
    rm -f /tmp/miniconda.sh
  fi
  PYTHON_CMD="$conda_dir/bin/python"
  [[ -x "$PYTHON_CMD" ]] || { log_error "Miniconda 安装失败"; exit 1; }

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

README 将“我要克隆音色”“帮我克隆我的声音”作为触发示例,但没有说明完整触发词范围、必须包含哪些意图信号,或哪些相似日常表达不应触发该流程。对于 markdown 技能说明,这种缺少约束的自然语言触发描述可能导致在 QQBOT 中被过宽匹配而误进入音色克隆工作流。

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

“用开心的语气朗读:…”和“生成语音:…”被作为用户说法示例,但文档未说明是否只有这些固定短语会触发,还是任意包含类似措辞的消息都可能命中。由于缺少负例或上下文限制,这类描述对 markdown 文件而言属于触发条件不够具体。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

README 中的配置示例将 defaultLanguage 固定为 Chinese,同时文件整体未说明用户是否可以选择其他语言或是否仅适用于特定中文场景。按规则,未提供用户语言/locale 选择或明确的合理地域限制,属于自然语言层面的语言策略风险。

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 193)May include surrounding context.

bash stop_tts.sh bash self_check.sh

curl http://127.0.0.1:5002/api/health curl http://127.0.0.1:9002/health

text

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file’s operational instructions and user-facing guidance are entirely in Chinese, and there is no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking deployment. Under the policy, forcing a specific language without opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This manifest sets defaultLanguage to Chinese, which is a natural-language locale choice applied by default. Under the policy, forcing a specific language without offering user choice or documenting a justified region-specific constraint is a policy violation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The installer modifies the host by installing systemd services and then starts local services automatically, but there is no visible consent prompt, dry-run mode, or explanation of exactly what services will be registered. In a skill package with no trusted metadata or stated purpose context, persistent service installation increases risk because it creates background execution and boot persistence on the user's machine.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script installs startup services and may immediately launch them without any explicit warning that it will change system boot behavior. This is dangerous because users may unknowingly grant persistence to software, making rollback harder and increasing the blast radius if any subordinate script or service is unsafe.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This shell script creates persistent user-level systemd service files and later enables them to start immediately and on login, which changes the user's system configuration. While there is a final success message, there is no warning, confirmation prompt, or explanatory comment/docstring before these file writes and service-enablement actions occur.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

This command enables and starts a per-user systemd service, causing the TTS component to persist across future user sessions. Persistence is security-relevant because any compromised or vulnerable service installed this way will automatically restart and remain available without further user action.

Content

Scanner excerpt · install_systemd_services.sh (reported line 60)May include surrounding context.

sh
EOF

systemctl --user daemon-reload
systemctl --user enable --now "$TTS_UNIT_NAME"
systemctl --user enable --now "$NODE_UNIT_NAME"
echo "xeontts 开机自启已启用"

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

This command enables and starts the Node gateway as a persistent per-user service. In skill context, this is more sensitive because it exposes a long-running gateway process via autostart; if the server code later has weaknesses or binds too broadly, persistence increases exposure and makes removal less obvious to users.

Content

Scanner excerpt · install_systemd_services.sh (reported line 61)May include surrounding context.

sh
systemctl --user daemon-reload
systemctl --user enable --now "$TTS_UNIT_NAME"
systemctl --user enable --now "$NODE_UNIT_NAME"
echo "xeontts 开机自启已启用"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This shell script emits multiple user-facing messages such as 健康检查通过, 检查本地服务, and 自检通过 entirely in Chinese. Under the policy, forcing a specific language without user opt-in or a documented regional justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · self_check.sh (reported line 78)May include surrounding context.

sh
fi

log_step "检查 systemd 状态"
if systemctl --user is-enabled xeontts-tts.service >/dev/null 2>&1; then pass "xeontts-tts.service 已启用"; else fail "xeontts-tts.service 未启用"; fi
if systemctl --user is-enabled xeontts-node.service >/dev/null 2>&1; then pass "xeontts-node.service 已启用"; else fail "xeontts-node.service 未启用"; fi

if [[ "$FAIL_COUNT" -gt 0 ]]; then

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · self_check.sh (reported line 79)May include surrounding context.

sh
fi

log_step "检查 systemd 状态"
if systemctl --user is-enabled xeontts-tts.service >/dev/null 2>&1; then pass "xeontts-tts.service 已启用"; else fail "xeontts-tts.service 未启用"; fi
if systemctl --user is-enabled xeontts-node.service >/dev/null 2>&1; then pass "xeontts-node.service 已启用"; else fail "xeontts-node.service 未启用"; fi

if [[ "$FAIL_COUNT" -gt 0 ]]; then

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The configuration sets defaultLanguage to Chinese, and later request handlers fall back to this value when no language is supplied. This creates a language/locale policy issue because the skill defaults users into a specific language rather than offering a neutral default or explicit opt-in.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 75)May include surrounding context.

sh
check_sudo() {
  if [[ "$EUID" -eq 0 ]]; then
    SUDO=""
  elif command -v sudo >/dev/null 2>&1; then
    SUDO="sudo"
  else
    SUDO=""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 84)May include surrounding context.

sh
check_sudo() {
  if [[ "$EUID" -eq 0 ]]; then
    SUDO=""
  elif command -v sudo >/dev/null 2>&1; then
    SUDO="sudo"
  else
    SUDO=""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 90)May include surrounding context.

sh
case "$os_id" in
    ubuntu|debian)
      log_step "安装系统依赖 (Debian/Ubuntu)"
      $SUDO apt-get update -qq >/dev/null 2>&1 || true
      $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg >/dev/null 2>&1 || \
        $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg
      ;;

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 91)May include surrounding context.

sh
case "$os_id" in
    ubuntu|debian)
      log_step "安装系统依赖 (Debian/Ubuntu)"
      $SUDO apt-get update -qq >/dev/null 2>&1 || true
      $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg >/dev/null 2>&1 || \
        $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg
      ;;

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 92)May include surrounding context.

sh
case "$os_id" in
    ubuntu|debian)
      log_step "安装系统依赖 (Debian/Ubuntu)"
      $SUDO apt-get update -qq >/dev/null 2>&1 || true
      $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg >/dev/null 2>&1 || \
        $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg
      ;;

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 98)May include surrounding context.

sh
case "$os_id" in
    ubuntu|debian)
      log_step "安装系统依赖 (Debian/Ubuntu)"
      $SUDO apt-get update -qq >/dev/null 2>&1 || true
      $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg >/dev/null 2>&1 || \
        $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg
      ;;

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · setup_env.sh (reported line 99)May include surrounding context.

sh
case "$os_id" in
    ubuntu|debian)
      log_step "安装系统依赖 (Debian/Ubuntu)"
      $SUDO apt-get update -qq >/dev/null 2>&1 || true
      $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg >/dev/null 2>&1 || \
        $SUDO apt-get install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg
      ;;

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · setup_env.sh (reported line 99)May include surrounding context.

sh
local pkg_mgr="yum"
      command -v dnf >/dev/null 2>&1 && pkg_mgr="dnf"
      $SUDO "$pkg_mgr" install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg which >/dev/null 2>&1 || \
        $SUDO "$pkg_mgr" install -y wget curl git lsof net-tools unzip bzip2 ca-certificates ffmpeg which
      ;;
    *)
      log_warn "未知系统,跳过系统依赖自动安装"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

When --force is used, the script unconditionally removes the entire $HOME/miniconda3 directory with rm -rf. Although the script has general logging, there is no explicit warning at the point of deletion, no confirmation prompt, and no comment/docstring disclosing that an existing Python environment will be destroyed.

Content

No source excerpt is available for this finding.