Back to skill

Security audit

AI语音合成TTS - 聚合数据

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward text-to-speech integration, but users should handle the API key and downloaded audio files carefully.

Prefer setting JUHE_SPEECH_KEY through your environment or platform secrets instead of using --key or scripts/.env. Do not synthesize confidential text unless you are comfortable sending it to Juhe, and use --download/--output only for paths you intend to create or replace.

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
scripts/speech_generate.py:106
Finding
Unvalidated Server-Controlled Audio Download URL## Vulnerability Details **File Location**: `scripts/speech_generate.py:106-110, 133-138, 320-337` **Vulnerability Type**: Unrestricted remote resource retrieval and unsafe file download **Risk Level**: Medium ```python if error_code == 0: result = data.get("result", {}) return { "success": True, "orderid": result.get("orderid", ""), "audio_url": result.get("audio_url", ""), } ``` ```python def download_audio(url: str, save_path: str) -> bool: """Download the audio file locally and return whether it succeeded.""" try: with urllib.request.urlopen(url, timeout=30) as resp: content = resp.read() Path(save_path).write_bytes(content) return True except Exception as e: print(f"Download failed: {e}") return False ``` ```python audio_url = result["audio_url"] orderid = result["orderid"] print(f"Audio synthesis succeeded.") print(f"Order ID: {orderid}") print(f"Audio URL:") print(f"{audio_url}") if parsed["download"] or parsed["output"]: if parsed["output"]: save_path = parsed["output"] else: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") save_path = f"speech_{timestamp}.wav" if download_audio(audio_url, save_path): size_kb = Path(save_path).stat().st_size // 1024 print(f"Download completed. File size: {size_kb} KB") ``` ### Technical Analysis The remote Juhe API controls the `audio_url` value. When downloading is enabled, the script passes this value directly to `urllib.request.urlopen` without validating: - The URL scheme - The destination hostname - Redirect destinations - Response size - Response content type - Whether the response is actually an audio file The script also reads the complete response into memory before writing it. A compromised provider, compromised API response path, or provider-side de ...[truncated 2040 chars]
Remediation
## Remediation Suggestions 1. Parse the URL before making the request and require the `https` scheme. 2. Allowlist the exact expected audio-storage hostname or a narrowly defined set of provider-owned hostnames. 3. Disable automatic redirects or validate the scheme and hostname of every redirect target. 4. Reject URLs containing credentials, unexpected ports, or ambiguous hostname representations. 5. Check `Content-Type` against expected audio media types, while recognizing that this is only a supplementary control. 6. Enforce a conservative maximum download size using `Content-Length` when available and a strict byte counter while streaming. 7. Stream the response in bounded chunks rather than calling `resp.read()` without a limit. 8. Write to a securely created temporary file and atomically move it into place after validation. 9. Refuse to overwrite an existing destination by default and reject symbolic-link destinations. 10. Use a restrictive file mode for generated files and report validation failures without attempting the write.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/speech_generate.py:172
Finding
API Key Exposure Through Command-Line Arguments## Vulnerability Details **File Location**: `scripts/speech_generate.py:172-176, 294-297`; documented at `SKILL.md:27-31` **Vulnerability Type**: Sensitive credential exposure through process arguments and shell history **Risk Level**: Low ```python if arg == "--key": if i + 1 < len(args): result["cli_key"] = args[i + 1] i += 2 else: result["error"] = "Error: --key requires an API key value" return result ``` ```python if not api_key: print("API key not found. Configure it using one of these methods:") print("1. Environment variable: export JUHE_SPEECH_KEY=your_api_key") print("2. .env file: create .env in the script directory") print("3. Command-line argument: python speech_generate.py --key your_api_key \"text\"") sys.exit(1) ``` The documentation also explicitly recommends the following supported method: ```bash python scripts/speech_generate.py --key yourAppKey "Text to synthesize" ``` ### Technical Analysis Supplying an API key through `--key` places the secret in the process argument vector. Depending on the operating system and execution environment, process arguments may be visible to other local users, process-monitoring tools, job runners, audit systems, or diagnostic collectors. The command may also remain in shell history and terminal logs after execution. The API request itself uses HTTPS and sending the key to the declared Juhe endpoint is necessary for the TTS operation. The vulnerability is the additional local exposure caused by accepting and documenting a command-line secret. ### Attack Path 1. A user follows the documented command and supplies the Juhe API key through `--key`. 2. The shell stores the command in history, or the operating system exposes it in the process list while the request is running. 3. Another local user, monitoring service, CI log collector, support bundle, or later reader of shell history ...[truncated 589 chars]
Remediation
## Remediation Suggestions 1. Remove the `--key` option and its examples from both the script and `SKILL.md`. 2. Prefer a protected environment variable or a credential file readable only by the owning user. 3. If interactive entry is necessary, use a non-echoing prompt such as Python's `getpass.getpass`. 4. Ensure the `.env` file is excluded from version control and recommend permissions equivalent to `0600`. 5. Avoid printing, logging, or embedding the key in exception messages. 6. Document credential rotation procedures for users who previously supplied the key on the command line. 7. Where supported, use a scoped credential restricted to only the required TTS API and apply usage or spending limits.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Credential Access

High
Category
Privilege Escalation
Content
# 方式一:环境变量(推荐,一次配置永久生效)
export JUHE_SPEECH_KEY=你的AppKey

# 方式二:.env 文件(在脚本目录创建)
echo "JUHE_SPEECH_KEY=你的AppKey" > scripts/.env

# 方式三:每次命令行传入
Confidence
87% confidence
Finding
The skill recommends storing the API key in a local .env file under the scripts directory, which is a credential-handling pattern that can lead to accidental exposure through repository inclusion, backup leakage, or other file access by tools. In a skill that already uses file and environment capabilities, colocating secrets with runnable content increases the blast radius if the workspace is exposed.

Credential Access

High
Category
Privilege Escalation
Content
export JUHE_SPEECH_KEY=你的AppKey

# 方式二:.env 文件(在脚本目录创建)
echo "JUHE_SPEECH_KEY=你的AppKey" > scripts/.env

# 方式三:每次命令行传入
python scripts/speech_generate.py --key 你的AppKey "今天天气真好!"
Confidence
83% confidence
Finding
The documented command-line usage includes passing the API key directly as a --key argument, which can expose credentials in shell history, process listings, logs, and agent telemetry. This is a genuine secret leakage risk, especially in automated environments where command invocations may be recorded.

Credential Access

High
Category
Privilege Escalation
Content
API Key 配置(任选其一,优先级从高到低):
    1. 环境变量: export JUHE_SPEECH_KEY=your_api_key
    2. 脚本同目录的 .env 文件: JUHE_SPEECH_KEY=your_api_key
    3. 直接传参: python speech_generate.py --key your_api_key "文本内容"

免费申请 API Key: https://www.juhe.cn/docs/api/id/830
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
API Key 配置(任选其一,优先级从高到低):
    1. 环境变量: export JUHE_SPEECH_KEY=your_api_key
    2. 脚本同目录的 .env 文件: JUHE_SPEECH_KEY=your_api_key
    3. 直接传参: python speech_generate.py --key your_api_key "文本内容"

免费申请 API Key: https://www.juhe.cn/docs/api/id/830
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
if env_key:
        return env_key

    env_file = Path(__file__).parent / ".env"
    if env_file.exists():
        for line in env_file.read_text(encoding="utf-8").splitlines():
            line = line.strip()
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents and requires capabilities that include environment access, file reads/writes, and outbound network use, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent invokes the skill with broader-than-necessary privileges, making misuse or unintended data access harder to constrain and audit.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The description contains broad natural-language trigger phrases like '帮我生成一段语音' and '用英文女声读一下这句话', which can overlap with common user requests and cause over-triggering. In an agent setting, this may route unrelated or sensitive text into the skill unexpectedly, leading to accidental third-party disclosure or unintended API use.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The skill does not present a clear warning that user-provided text will be transmitted to a third-party TTS provider. Because users may submit private, regulated, or proprietary text for synthesis, the lack of disclosure creates a real risk of unintentional external data sharing.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
该脚本的自然语言说明、示例和运行时交互文本均固定为中文,且没有提供语言/locale 选择开关。根据规则,强制特定语言而无用户选择可能构成自然语言组织政策违规,尤其是当技能并未声明仅面向中文用户时。

Static analysis

No suspicious patterns detected.