T09 · Insecure Skill Coding Practices
- Location
SKILL.md:39- Finding
Shell Command Injection Through Unquoted User-Controlled Parameters
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, line 39
Vulnerability Type: Shell command injection
Risk Level: HighVulnerable Code:
bash python3 {baseDir}/scripts/draw_animal.py --animal {animal:-pig} --lang {lang:-en}Technical Analysis
The Skill instructs the Agent to insert the user-derived
animalandlangparameters directly into a shell command without shell-safe quoting or a structured argument-array invocation. If the execution environment performs textual interpolation and passes the resulting command to a shell, shell metacharacters in either parameter can terminate or alter the intended command and introduce additional commands.The validation in
scripts/draw_animal.pydoes not mitigate this issue. Shell parsing and command substitution occur before the Python process receives and validates its arguments. Consequently, the substring denylist invalidate_animal()cannot inspect or block commands that the shell has already interpreted. Thelangargument also has no protection before shell parsing; itsargparsechoices are enforced only after Python starts.Exploitability depends on whether the host executes the documented command through a shell using direct placeholder interpolation. Under that execution model, the flaw crosses the boundary from harmless text generation to arbitrary command execution.
Attack Path
- An attacker submits an animal or language value containing shell control syntax.
- The Agent extracts the attacker-controlled value as the
animalorlangparameter. - The value is interpolated into the command shown in
SKILL.mdwithout shell-safe encoding. - The host shell parses the injected metacharacters before launching
draw_animal.py. - The shell executes attacker-selected commands under the account and permissions of the Agent or Skill runner.
- Python-side validation either runs only on the remaining arguments or is never reached, so ...[truncated 980 chars]
- Remediation
View remediation
Remediation Suggestions
- Invoke the script through a process API that accepts a structured argument array rather than constructing a shell command. For example, pass arguments equivalent to:
text ["python3", "{baseDir}/scripts/draw_animal.py", "--animal", animal, "--lang", lang] - Disable shell execution for this invocation. In Python-based launchers, use
subprocess.run([...], shell=False, check=True)or an equivalent API. - Validate parameters before process creation. Allowlist the supported animal names and restrict
langtoenorzh. - If unknown animal names must remain supported, apply a strict character and length policy, such as letters, spaces, and hyphens only, rather than relying on a restricted-word substring denylist.
- If shell use is unavoidable, apply platform-appropriate shell escaping to every dynamic argument. Quoting is defense in depth and should not replace structured invocation and allowlist validation.
- Retain
argparsevalidation inside the script as a secondary control, but do not treat post-shell validation as protection against command injection.
- Invoke the script through a process API that accepts a structured argument array rather than constructing a shell command. For example, pass arguments equivalent to:
