T09 · Insecure Skill Coding Practices
- Location
scripts/transcribe.sh:80- Finding
Curl Multipart Form Injection Allows Unintended Local File Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
scripts/transcribe.sh, lines 37–38 and 80
Vulnerability Type: Curl multipart form-string injection
Risk Level: MediumVulnerable Code
bash --prompt) prompt="${2:-}" shift 2 ;;bash ${prompt:+-F "prompt=${prompt}"} \Technical Analysis
The script accepts an arbitrary value through the
--promptargument and passes it to curl using-F. Curl treats-Fvalues 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/passwdis 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 inSKILL.mdthat the wrapper sanitizes user-controlled parameters.The same hardening principle applies to the other textual multipart fields—
model,language, andresponse_format—which should also use curl's literal form-string option. The intended audio field must continue using file-upload syntax.Attack Path
-
An attacker gains influence over arguments supplied to
transcribe.sh, such as through an automation workflow, agent-generated command, or untrusted user request. -
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 -
The script stores the value without validating or escaping curl multipart control syntax.
-
The script invokes curl with:
bash -F "prompt=@/path/to/readable/sensitive-file" -
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
View remediation
Remediation Suggestions
Use curl's
--form-stringoption for every multipart field that must be treated as literal text. Reserve-Ffile 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/passwdand</etc/passwd, and verify that these strings are transmitted literally rather than causing curl to read the referenced files.
