T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:71
- Finding
- Shell Command Injection Through Unsafe User-Input Substitution<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:71` **Related Instances**: `SKILL.md:32`, `SKILL.md:77`, `SKILL.md:103`, `SKILL.md:117` **Vulnerability Type**: Shell command injection **Risk Level**: High ### Vulnerable Code ```bash obsidian-cli daily && obsidian-cli create "$(date +%Y-%m-%d).md" --content "$(printf '\n%s' "ENTRY_TEXT")" --append ``` Other documented commands use the same unsafe substitution pattern for user-controlled vault names, folders, search terms, and entry text: ```bash obsidian-cli set-default "VAULT_NAME" ``` ```bash obsidian-cli daily && obsidian-cli create "Daily Notes/$(date +%Y-%m-%d).md" --content "$(printf '\n%s' "ENTRY_TEXT")" --append ``` ```bash obsidian-cli search-content "TERM" ``` ```bash obsidian-cli print "2025-01-10.md" --vault "NAME" ``` ### Technical Analysis The Skill presents shell templates whose placeholders are expected to be replaced with values supplied by a user. It does not require shell-safe argument handling, escaping, or input validation. If an agent performs direct textual substitution before passing the resulting command to a shell, surrounding a placeholder with double quotes is insufficient protection. Shell constructs such as command substitution remain active inside double-quoted strings, and embedded quotation marks can terminate the intended argument. For example, substituting the following value for `ENTRY_TEXT`: ```text $(touch /tmp/obsidian-skill-injected) ``` can produce: ```bash obsidian-cli daily && obsidian-cli create "$(date +%Y-%m-%d).md" --content "$(printf '\n%s' "$(touch /tmp/obsidian-skill-injected)")" --append ``` The shell evaluates the nested command substitution before invoking `printf` or `obsidian-cli`. The vulnerability is therefore not limited to malformed note content: it can cause arbitrary operating-system commands to run. Exploitation depends on the agent constructing shell source through direct placeholder replacement. The Skill provides ...[truncated 1255 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not replace placeholders by concatenating or interpolating user-controlled text into shell source. 2. Invoke `obsidian-cli` through a process API that accepts an argument array and does not invoke a shell. Pass entry text, vault names, paths, and search terms as independent arguments. 3. If a shell is unavoidable, pass untrusted values through positional parameters or environment variables rather than embedding them into the command text. Quote every expansion: ```bash entry_text="$1" obsidian-cli daily && obsidian-cli create "$(date +%Y-%m-%d).md" \ --content "$(printf '\n%s' "$entry_text")" \ --append ``` The caller must supply `$1` as a distinct argument rather than constructing another shell command containing the value. 4. Apply strict allowlist validation to structured fields: - Validate date values against the configured date format. - Restrict vault names to known configured vaults. - Normalize note paths and reject absolute paths, traversal components such as `..`, control characters, and paths outside the intended vault. 5. Treat note entries and search terms as opaque data. Do not evaluate them, feed them to `eval`, or insert them into command templates. 6. Add an explicit security warning to the Skill stating that placeholders must never be replaced through textual shell-command construction. 7. Add regression tests using inputs containing command substitutions, quotation marks, semicolons, newlines, backticks, and other shell metacharacters, verifying that they are passed literally and cause no filesystem or process side effects. ]]>
