T09 · Insecure Skill Coding Practices
Error
- Location
- discovery.md:15
- Finding
- Shell Command Injection Through Unencoded Search Terms<![CDATA[ ## Vulnerability Details **File Locations**: - `discovery.md:15-17` - `discovery.md:34-37` - `discovery.md:48-51` **Vulnerability Type**: Shell command injection through unsafe interpolation of user-controlled search terms **Risk Level**: High ### Vulnerable Code ```bash curl -s "https://huggingface.co/api/models?search=<term>&limit=20" | jq '.[].id' ``` ```bash curl -s "https://huggingface.co/api/datasets?search=<term>&limit=20" | jq '.[].id' ``` ```bash curl -s "https://huggingface.co/api/spaces?search=<term>&limit=20" | jq '.[].id' ``` ### Technical Analysis The discovery workflow instructs the agent to replace the `<term>` placeholder inside executable shell command text. No requirement is provided to URL-encode the value or pass it to `curl` through a safely populated variable. Although the placeholder appears inside double quotes, shell substitutions such as `$(command)` and backtick command substitution are still evaluated within double-quoted strings when the resulting command is parsed by a shell. An attacker-controlled search term can therefore become executable shell syntax if the agent performs direct textual substitution before invoking the command. Characters such as quotes, semicolons, newlines, and shell substitutions may also modify the intended command structure. URL encoding alone is insufficient if it occurs only after unsafe shell parsing; the untrusted value must be supplied as data rather than inserted into shell source code. The same vulnerable construction is used for model, dataset, and Space discovery. ### Attack Path 1. An attacker asks the agent to search Hugging Face using a maliciously constructed term, such as a value containing `$(malicious_command)`. 2. The agent follows `discovery.md` and directly replaces `<term>` with the supplied text. 3. The generated command is passed to a shell. 4. The shell evaluates the embedded command substitution before starting `curl`. 5. The injected command executes locally wi ...[truncated 995 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not replace placeholders inside shell command source with untrusted text. Populate a shell variable through a non-evaluating input channel and use `curl --get` with `--data-urlencode`: ```bash # TERM must be populated as data, such as from a safely passed positional # argument, rather than inserted into this script's source text. TERM=$1 curl -sS --fail --get \ --data-urlencode "search=${TERM}" \ --data-urlencode "limit=20" \ "https://huggingface.co/api/models" | jq '.[].id' ``` Apply the same pattern to the dataset and Space endpoints: ```bash curl -sS --fail --get \ --data-urlencode "search=${TERM}" \ --data-urlencode "limit=20" \ "https://huggingface.co/api/datasets" | jq '.[].id' curl -sS --fail --get \ --data-urlencode "search=${TERM}" \ --data-urlencode "limit=20" \ "https://huggingface.co/api/spaces" | jq '.[].id' ``` Additional hardening measures should include: 1. Explicitly instruct the agent never to interpolate user input into shell command text. 2. Prefer an HTTP client tool with structured URL and query-parameter fields over a shell command. 3. Pass arguments directly to a process-execution API without invoking a shell where supported. 4. Retain `--fail` so HTTP failures are not silently treated as valid discovery results. 5. Validate search-term length and reject control characters or unexpected newlines as defense in depth. 6. Add regression tests using terms containing spaces, quotes, `$()`, backticks, semicolons, ampersands, and newlines, verifying that none are interpreted by a shell. ]]>
