T09 · Insecure Skill Coding Practices
- Location
SKILL.md:29- Finding
Shell Command Injection Through Unsafely Interpolated Search Queries
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 29-45
Vulnerability Type: Shell command injection
Risk Level: HighThe skill instructs the agent to insert a user-provided topic or brand directly into shell commands:
bash node tools/clis/exa.js search --query "[topic or brand]" --category "tweet" --num-results 20For recent tweets only:
bash node tools/clis/exa.js search --query "[topic]" --category "tweet" --num-results 20 --start-date [current-year]-01-01 # Use current yearThe dry-run command uses the same unsafe construction:
bash node tools/clis/exa.js search --query "[topic]" --category "tweet" --dry-runTechnical Analysis
The search topic originates from user-controlled input and is interpolated into a command that the skill explicitly directs the agent to run through
exec. Enclosing the value in double quotes does not make shell interpolation safe. POSIX-compatible shells still evaluate command substitutions such as$(command)and backtick expressions inside double-quoted strings.Consequently, a crafted topic can alter execution before the Node.js process receives the query argument. The vulnerability affects normal searches, date-filtered searches, and dry runs. The
--dry-runoption only changes the Exa CLI's behavior; it does not prevent the shell from evaluating injected substitutions first.For example, if an untrusted topic contains a command-substitution expression, the resulting shell command executes that expression locally while also passing its output as part of the search query.
Attack Path
- An attacker asks the agent to search X/Twitter for a topic containing shell metacharacters or command substitution, such as a value shaped like
brand $(attacker-command). - The agent follows
SKILL.mdand replaces the[topic]placeholder with the supplied text. - The agent submits the resulting command through a shell-backed
execo ...[truncated 1387 chars]
- An attacker asks the agent to search X/Twitter for a topic containing shell metacharacters or command substitution, such as a value shaped like
- Remediation
View remediation
Remediation Suggestions
-
Do not interpolate user input into a shell command string. Invoke Node.js with an argument-array API that bypasses shell parsing, such as:
javascript execFile("node", [ "tools/clis/exa.js", "search", "--query", topic, "--category", "tweet", "--num-results", "20" ]); -
Update the skill instructions to require a non-shell process execution interface. Explicitly state that the topic must be passed as one argument and that shell execution must be disabled.
-
Apply the same correction to the normal, recent-results, and dry-run workflows. A dry run must not be represented as safe when its command is still parsed by a shell.
-
If the available execution tool only accepts shell strings, use a well-tested platform-specific argument-escaping library rather than manual quoting. Reject command-substitution syntax and shell control characters as defense in depth, but do not treat validation alone as a substitute for argument-array execution.
-
Add adversarial tests using topics containing spaces, quotes, semicolons, newlines, backticks,
$(), pipes, and redirection operators. Verify that every value reaches the CLI as inert data and never causes an additional process or shell operation. -
Run the agent and CLI with least privilege, narrowly scoped credentials, restricted filesystem access, and constrained outbound network access to reduce impact if command construction regresses.
-
