T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:404
- Finding
- Shell Command Injection Through Inline User-Controlled JSON<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:404-412` **Additional Locations**: `SKILL.md:424-425`, `SKILL.md:445-446`, `SKILL.md:773-779`, `SKILL.md:852-859`, `SKILL.md:877` **Vulnerability Type**: Shell command injection **Risk Level**: High ### Vulnerable Code ```bash ### Step 5a: Upload JSON Only RUN THIS SCRIPT: ```bash ./scripts/push-to-github.sh <KW> <YEAR> '<JSON>' ``` ``` The same unsafe argument pattern is used for employee updates: ```bash - Push updated employees.json: `update-employees.sh '<JSON>'` ``` ### Technical Analysis The Skill instructs the Agent to insert generated JSON directly into a shell command as a single-quoted argument. That JSON can contain data originating from untrusted CSV files and user messages, including: - Employee names - Email addresses - CSV comments - Employee `info` fields - Company and roster text A single quote inside any attacker-controlled value terminates the shell's quoted string before either script begins execution. Additional shell syntax can then be interpreted as a separate command. For example, an attacker-controlled text value conceptually shaped like: ```text '; attacker-command; # ``` can break out of the intended JSON argument if the Agent substitutes it directly into the documented command template. The scripts validate JSON after startup, but this does not mitigate the vulnerability. Shell parsing and command substitution occur before `push-to-github.sh` or `update-employees.sh` receives its arguments. Consequently, injected commands can execute even when the remaining JSON is invalid. The underlying scripts already support file and standard-input modes, but the primary Skill instructions repeatedly require the unsafe inline-argument form. ### Attack Path 1. An attacker submits a CSV containing a crafted name, comment, email address, or other textual field with a single quote and shell metacharacters. 2. The Agent parses the value and includes it in roster JSON or ...[truncated 1368 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Remove all inline JSON command templates.** Do not place generated or user-controlled JSON inside a shell command string, regardless of quoting style. 2. **Use a securely created file and direct process invocation.** Write JSON through a trusted file API and invoke the script with an argument array rather than shell interpolation: ```text execve("./scripts/push-to-github.sh", ["push-to-github.sh", kw, year, safe_file_path], env) ``` 3. **Prefer standard input when supported.** Pass the JSON bytes directly to the child process's standard input without constructing a shell pipeline from text. 4. **Do not attempt to solve this only with shell escaping.** Avoid generating shell source entirely. Structured arguments or standard input eliminate the relevant parsing boundary. 5. **Validate structured fields before serialization.** Apply length and character constraints to employee keys, names, email addresses, week numbers, years, and repository identifiers. 6. **Add strict numeric validation** for calendar week and year in all scripts before using them to construct paths. 7. **Add adversarial tests** covering single quotes, double quotes, backticks, semicolons, newlines, `$()`, `${...}`, redirection operators, and Unicode control characters. 8. **Run the Agent with least privilege.** Use a dedicated operating-system account and a fine-grained, short-lived GitHub token restricted to the single roster repository with only required content and workflow permissions. ]]>
