T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:121
- Finding
- Shell Command Injection Through Unsafely Interpolated TTS Preview Text<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 121–136 **Vulnerability Type**: Shell command injection caused by unsafe interpolation of user-controlled data **Risk Level**: High ### Vulnerable Code ```bash curl -s -X POST https://api.senseaudio.cn/v1/t2a_v2 \ -H "Authorization: Bearer $SENSEAUDIO_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"model\": \"SenseAudio-TTS-1.0\", \"text\": \"<PREVIEW_TEXT>\", \"stream\": false, \"voice_setting\": { \"voice_id\": \"<VOICE_ID>\" }, \"audio_setting\": { \"format\": \"mp3\" } }" -o preview.json jq -r '.data.audio' preview.json | xxd -r -p > preview_<VOICE_ID>.mp3 ``` ### Technical Analysis The Skill instructs the agent to substitute a user-supplied preview sentence directly into a double-quoted shell argument. Double quotes do not suppress command substitution in common shells: constructs such as `$(...)` and backticks are evaluated before `curl` executes. JSON escaping alone is insufficient because shell parsing occurs before the data is passed to `curl`. Consequently, preview text containing shell metacharacters can cause arbitrary commands to run locally. The output filename also interpolates `<VOICE_ID>` without shell-safe quoting. The documented workflow normally obtains this value from a fixed voice catalog, which limits practical exposure, but the implementation should still enforce an allowlist and quote the resulting path. ### Attack Path 1. An attacker asks the agent to generate a TTS preview. 2. The attacker supplies preview text containing command-substitution syntax, such as a sentence that embeds `$(id)`. 3. The agent follows the Skill and replaces `<PREVIEW_TEXT>` inside the provided shell command. 4. The shell evaluates the embedded command substitution before invoking `curl`. 5. The injected command executes with the same operating-system privileges and environment as the agent. 6. An attacker could extend this primitive to read acc ...[truncated 1007 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not construct JSON by interpolating user-controlled values into a shell command. Use `jq` or a programming-language JSON serializer to generate the request body as data: ```bash jq -n \ --arg text "$PREVIEW_TEXT" \ --arg voice "$VOICE_ID" \ '{ model: "SenseAudio-TTS-1.0", text: $text, stream: false, voice_setting: {voice_id: $voice}, audio_setting: {format: "mp3"} }' > request.json curl --fail-with-body -sS -X POST \ https://api.senseaudio.cn/v1/t2a_v2 \ -H "Authorization: Bearer $SENSEAUDIO_API_KEY" \ -H "Content-Type: application/json" \ --data-binary @request.json \ -o preview.json ``` Apply the following additional controls: 1. Store preview text in a variable without evaluating it and always quote variable expansions. 2. Validate `VOICE_ID` against the exact voice IDs listed in the Skill rather than accepting arbitrary values. 3. Generate output paths in a controlled directory and use a fixed mapping from approved voice IDs to filenames. 4. Quote the output path, for example: ```bash output_file="previews/preview_${VOICE_ID}.mp3" jq -r '.data.audio' preview.json | xxd -r -p > "$output_file" ``` 5. Check the API response type and status before decoding it, and reject missing, malformed, or unexpectedly large audio fields. 6. Create temporary request and response files with restrictive permissions and unpredictable names, or avoid intermediate files entirely. 7. Prefer an HTTP client library over dynamically assembled shell commands when the execution environment supports one. ]]>
