T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:24
- Finding
- Shell Command Injection Through Unsafely Interpolated Graphiti Input<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:24-30`, `SKILL.md:38-43`, `SKILL.md:64-70`, and `SKILL.md:75-80` **Vulnerability Type**: Shell command injection caused by embedding variable content in single-quoted JSON shell arguments **Risk Level**: High ### Vulnerable Code Search operation (`SKILL.md:24-30`): ```bash bash command:" GRAPHITI_URL=\$({baseDir}/references/env-check.sh) curl -s -X POST \"\$GRAPHITI_URL/facts/search\" \ -H 'Content-Type: application/json' \ -d '{\"query\": \"YOUR_QUERY\", \"max_facts\": 10}' | jq . " ``` Episode creation operation (`SKILL.md:38-43`): ```bash bash command:" GRAPHITI_URL=\$({baseDir}/references/env-check.sh) curl -s -X POST \"\$GRAPHITI_URL/messages\" \ -H 'Content-Type: application/json' \ -d '{\"name\": \"EPISODE_NAME\", \"content\": \"EPISODE_CONTENT\"}' | jq . " ``` The same unsafe command construction is repeated in the examples at `SKILL.md:64-70` and `SKILL.md:75-80`. ### Technical Analysis The skill instructs the agent to replace `YOUR_QUERY`, `EPISODE_NAME`, and `EPISODE_CONTENT` directly inside JSON enclosed by a single-quoted shell argument. JSON escaping and shell escaping are separate security boundaries. An apostrophe in attacker-controlled content terminates the shell's single-quoted argument, after which shell metacharacters can introduce additional commands. For example, if an episode value is substituted with content structurally resembling: ```text x'; id > /tmp/graphiti-injection; # ``` the apostrophe can close the argument, the semicolon can terminate the `curl` command fragment, and the remaining text can be parsed as a new shell command. The exact payload may require adjustment for the command wrapper used by the agent, but the underlying unsafe composition permits shell syntax to escape the intended JSON value. The issue affects both read and write workflows because graph search terms and episode fields can originate from user instructions. Quoting the URL ...[truncated 1634 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not create JSON request bodies by inserting user-controlled text into shell command templates. Keep untrusted values in shell variables and use `jq` to perform JSON encoding: ```bash query="$1" payload=$(jq -n --arg query "$query" --argjson max_facts 10 \ '{query: $query, max_facts: $max_facts}') curl --fail --silent --show-error \ -X POST "$GRAPHITI_URL/facts/search" \ -H 'Content-Type: application/json' \ --data-binary "$payload" ``` For episode creation: ```bash name="$1" content="$2" payload=$(jq -n --arg name "$name" --arg content "$content" \ '{name: $name, content: $content}') curl --fail --silent --show-error \ -X POST "$GRAPHITI_URL/messages" \ -H 'Content-Type: application/json' \ --data-binary "$payload" ``` Additional hardening should include: 1. Pass user values through positional parameters or environment variables rather than substituting them into executable command text. 2. Avoid `eval`, nested shell construction, or any mechanism that reparses generated strings as commands. 3. Apply strict input-size limits to graph queries, names, and episode content. 4. Run the skill under a dedicated, least-privileged operating-system account. 5. Limit outbound network access to approved Graphiti endpoints. 6. Prefer authenticated HTTPS endpoints over plaintext HTTP for non-local deployments. 7. Add regression tests containing apostrophes, semicolons, command substitutions, newlines, backticks, and JSON control characters to verify that all input remains data rather than shell syntax. 8. Update every duplicated command example in `SKILL.md`; leaving an unsafe example may cause the agent to reproduce the vulnerable pattern. ]]>
