T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/transcribe.sh:78
- Finding
- Unquoted Optional curl Arguments Allow Argument and URL Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/transcribe.sh`, lines 78–79 **Vulnerability Type**: Shell argument injection through unsafe parameter expansion **Risk Level**: High ### Vulnerable Code ```bash curl -sS https://api.openai.com/v1/audio/transcriptions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Accept: application/json" \ -F "file=@${in}" \ -F "model=${model}" \ -F "response_format=${response_format}" \ ${language:+-F "language=${language}"} \ ${prompt:+-F "prompt=${prompt}"} \ >"$out" ``` ### Technical Analysis The optional `language` and `prompt` curl arguments are constructed using unquoted parameter expansions: ```bash ${language:+-F "language=${language}"} ${prompt:+-F "prompt=${prompt}"} ``` Because the outer expansions are not safely passed as array elements, attacker-controlled values are subject to shell word splitting and pathname expansion. Quote characters appearing within the replacement expression do not make this pattern equivalent to passing a preconstructed argument array. If an attacker can influence `--language` or `--prompt`, a crafted whitespace-delimited value may be interpreted as additional curl arguments rather than solely as multipart field content. This can modify curl behavior, introduce another URL, or manipulate output handling. The curl invocation applies the following authorization header globally: ```bash -H "Authorization: Bearer $OPENAI_API_KEY" ``` Consequently, an injected destination may receive the OpenAI API key if curl processes an attacker-selected URL while retaining the configured header. ### Attack Path 1. An attacker gains control over a value supplied to `--prompt` or `--language`, directly or through an application that invokes this script. 2. The attacker supplies a crafted value containing whitespace-separated curl arguments, such as options that introduce an additional URL. 3. The unquoted parameter expansion allows the shell to split the crafted v ...[truncated 1244 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Construct every curl argument using a Bash array so each user-controlled value remains exactly one argument: ```bash curl_args=( -sS --fail-with-body -H "Authorization: Bearer $OPENAI_API_KEY" -H "Accept: application/json" -F "file=@${in}" -F "model=${model}" -F "response_format=${response_format}" ) if [[ -n "$language" ]]; then curl_args+=(-F "language=${language}") fi if [[ -n "$prompt" ]]; then curl_args+=(-F "prompt=${prompt}") fi curl "${curl_args[@]}" \ "https://api.openai.com/v1/audio/transcriptions" \ >"$out" ``` Additional hardening measures: 1. Validate `language` against the expected language-code syntax or an explicit allowlist. 2. Apply reasonable length limits to `prompt`, `language`, and `model`. 3. Keep the destination URL fixed and separate from user-controlled arguments. 4. Use `--fail-with-body` so HTTP failures are not silently written as successful transcript files. 5. Write responses to a temporary file and rename it atomically only after curl succeeds. 6. Restrict the API key to the minimum required project permissions and configure usage limits. 7. Rotate the API key if this script has already processed values from untrusted sources. ]]>
