T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/transcribe.sh:80
- Finding
- Curl Multipart Form Injection Allows Unintended Local File Disclosure## Vulnerability Details **File Location**: `scripts/transcribe.sh`, lines 37–38 and 80 **Vulnerability Type**: Curl multipart form-string injection **Risk Level**: Medium ### Vulnerable Code ```bash --prompt) prompt="${2:-}" shift 2 ;; ``` ```bash ${prompt:+-F "prompt=${prompt}"} \ ``` ### Technical Analysis The script accepts an arbitrary value through the `--prompt` argument and passes it to curl using `-F`. Curl treats `-F` values as multipart form specifications rather than guaranteed literal strings. In particular, a value beginning with `@` instructs curl to read and upload a local file, while a value beginning with `<` instructs it to read a local file and submit its contents as a form field. Consequently, a prompt such as `@/etc/passwd` is not transmitted as literal prompt text. Curl interprets it as a file reference and reads the specified file into the outbound request. This contradicts the claim in `SKILL.md` that the wrapper sanitizes user-controlled parameters. The same hardening principle applies to the other textual multipart fields—`model`, `language`, and `response_format`—which should also use curl's literal form-string option. The intended audio field must continue using file-upload syntax. ### Attack Path 1. An attacker gains influence over arguments supplied to `transcribe.sh`, such as through an automation workflow, agent-generated command, or untrusted user request. 2. The attacker supplies a local path using curl's multipart metasyntax: ```bash ./scripts/transcribe.sh /path/to/audio.m4a \ --prompt @/path/to/readable/sensitive-file ``` 3. The script stores the value without validating or escaping curl multipart control syntax. 4. The script invokes curl with: ```bash -F "prompt=@/path/to/readable/sensitive-file" ``` 5. Curl reads the referenced local file and includes it in the HTTPS multipart request sent to `https://api.openai.com/v1 ...[truncated 993 chars]
- Remediation
- ## Remediation Suggestions Use curl's `--form-string` option for every multipart field that must be treated as literal text. Reserve `-F` file syntax exclusively for the intentional audio upload. Replace the curl invocation with: ```bash curl -sS https://api.openai.com/v1/audio/transcriptions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Accept: application/json" \ -F "file=@${in}" \ --form-string "model=${model}" \ --form-string "response_format=${response_format}" \ ${language:+--form-string "language=${language}"} \ ${prompt:+--form-string "prompt=${prompt}"} \ >"$out" ``` Because conditional parameter expansion can make argument boundaries difficult to review, a Bash array is preferable: ```bash curl_args=( -sS "https://api.openai.com/v1/audio/transcriptions" -H "Authorization: Bearer $OPENAI_API_KEY" -H "Accept: application/json" -F "file=@${in}" --form-string "model=${model}" --form-string "response_format=${response_format}" ) if [[ -n "$language" ]]; then curl_args+=(--form-string "language=${language}") fi if [[ -n "$prompt" ]]; then curl_args+=(--form-string "prompt=${prompt}") fi curl "${curl_args[@]}" >"$out" ``` Add regression tests using values such as `@/etc/passwd` and `</etc/passwd`, and verify that these strings are transmitted literally rather than causing curl to read the referenced files.
