T09 · Insecure Skill Coding Practices
- Location
SKILL.md:120- Finding
Shell Command Injection Through Unescaped Recipe and Ingredient Input
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 5, 112–122, 130–141, and 224–226
Vulnerability Type: Shell command injection caused by unsafe interpolation of user-controlled data
Risk Level: HighVulnerable Code
The Skill grants access to the general-purpose Bash tool:
yaml allowed-tools: [Bash]It then provides shell templates in which ingredient and dish values are expected to be populated from user input:
bash curl -s -X POST https://api.yummy.chat/ingredients \ -H "Content-Type: application/json" \ -d '{"ingredients": ["食材1", "食材2"]}'bash curl -s -X POST https://api.yummy.chat/howtocook \ -H "Content-Type: application/json" \ -d '{"dish_name": "菜名"}'The elevated exposure is reinforced by the tool configuration:
text - Uses `Bash` tool to execute `curl` commands - Pre-authorized in `allowed-tools` to reduce permission promptsTechnical Analysis
Ingredient and dish names are derived from user-controlled requests. The documented Bash commands place those values inside a single-quoted shell argument without requiring shell escaping, JSON serialization, input validation, or positional parameter handling.
A single quote in an input value can terminate the shell string containing the JSON request body. Subsequent shell metacharacters can then be interpreted as command syntax rather than recipe data. JSON escaping alone would not prevent this issue because JSON and shell quoting are separate parsing layers.
The Skill also instructs the Agent to pass Chinese input directly to the API. Therefore, there is no guaranteed normalization stage that would remove quotes or shell control characters. Because unrestricted Bash is pre-authorized, exploitation could lead directly to local command execution under the Agent process identity.
Attack Path
- An attacker submits a recipe or ingredient name containing a single quote followed ...[truncated 1473 chars]
- Remediation
View remediation
Remediation Suggestions
-
Replace unrestricted Bash with a narrowly scoped HTTP request tool that accepts structured JSON parameters and does not invoke a shell.
-
Remove
Bashfromallowed-toolsif it is not strictly necessary. Do not pre-authorize a general-purpose command execution tool for a simple API client. -
If Bash is unavoidable, serialize request bodies with a trusted JSON encoder and pass user values as data rather than source code. For example:
bash payload="$(jq -n --arg dish_name "$1" '{"dish_name": $dish_name}')" curl --fail-with-body --silent --show-error \ -X POST 'https://api.yummy.chat/howtocook' \ -H 'Content-Type: application/json' \ --data-binary "$payload"The user-controlled value must be supplied as a positional argument by a non-shell caller, not concatenated into the command text.
-
Apply the same safe serialization approach to ingredient arrays, such as using
jq --argsor constructing the JSON in the Agent's HTTP client. -
Validate inputs by enforcing documented length and item-count limits. Reject control characters and unexpected binary data, while treating validation only as defense in depth rather than a replacement for safe argument handling.
-
Add explicit instructions prohibiting direct interpolation of user-controlled strings into Bash commands.
-
Use
curl --fail-with-body --silent --show-errorand enforce connection and total timeouts so HTTP failures are handled safely and visibly. -
Add regression tests with quotation marks, backslashes, command separators, substitutions, and newline characters to verify that all values remain inert request data.
-
