T09 · Insecure Skill Coding Practices
- Location
SKILL.md:65- Finding
Shell Command Injection Through Unescaped Image Prompt Substitution
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 65-78
Vulnerability Type: Shell command injection caused by unsafe interpolation into a single-quoted command argument
Risk Level: HighAffected code:
bash export OATDA_API_KEY="${OATDA_API_KEY:-$(cat ~/.oatda/credentials.json 2>/dev/null | jq -r '.profiles[.defaultProfile].apiKey' 2>/dev/null)}" && \ curl -s -X POST "https://oatda.com/api/v1/llm/generate-image" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OATDA_API_KEY" \ -d '{ "provider": "<PROVIDER>", "model": "<MODEL>", "prompt": "<IMAGE_DESCRIPTION>", "size": "1024x1024", "quality": "standard", "n": 1, "numberOfImages": 1 }'Technical Analysis
The skill instructs the agent to substitute user-derived image descriptions directly into a JSON document enclosed by shell single quotes. No safe JSON encoder, file-based request body, or shell argument construction mechanism is specified.
If the agent performs literal placeholder substitution, an apostrophe in the image description terminates the shell's single-quoted
-dargument. The remaining prompt text is then parsed as shell syntax. Shell metacharacters such as semicolons, command substitutions, redirections, and comments can consequently turn prompt content into commands.JSON escaping alone is insufficient because shell quoting is evaluated before
curlprocesses the JSON. The value must be encoded without ever being reinterpreted as shell source.The nearby credential-file access is consistent with authenticating the declared image-generation API and prefers the existing environment variable. It does not, by itself, demonstrate privilege escalation. However, successful command injection would execute after
OATDA_API_KEYhas been exported and could access that credential and any other data available to the agent process.Attack
...[truncated 1759 chars]
- Remediation
View remediation
Remediation Suggestions
- Never interpolate user-controlled values into shell program text.
- Construct the request body with
jq --arg, which safely performs JSON encoding while passing input as a separate shell argument:bash payload="$( jq -n \ --arg provider "$PROVIDER" \ --arg model "$MODEL" \ --arg prompt "$IMAGE_DESCRIPTION" \ '{ provider: $provider, model: $model, prompt: $prompt, size: "1024x1024", quality: "standard", n: 1, numberOfImages: 1 }' )" || exit 1 curl --fail-with-body --silent --show-error \ -X POST "https://oatda.com/api/v1/llm/generate-image" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OATDA_API_KEY" \ --data-binary "$payload" - Pass all dynamic values through environment variables or positional arguments rather than inserting them into a generated command string.
- Do not use
eval,sh -c, or equivalent secondary shell evaluation. - Validate provider and model against strict allowlists, while treating the prompt as opaque data with the documented length limit.
- Stop execution when credential resolution fails or returns
null; use restrictive permissions such as0600for the credential file. - Avoid logging complete commands or authorization headers, and ensure diagnostic output cannot expose the API key.
- Add regression tests containing apostrophes, command substitutions, semicolons, newlines, backslashes, and Unicode characters, verifying that they remain literal JSON prompt content and never execute locally.
