T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:129
- Finding
- Unsafe Shell and JSON Interpolation of Untrusted Reply Content<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 129–138 **Vulnerability Type**: Command injection and malformed JSON caused by unsafe interpolation **Risk Level**: Medium ### Vulnerable Code ```bash gh api repos/<owner>/<repo>/issues/<id>/comments -f body='<reply>' ``` ```bash TOKEN=$(gh auth token) curl --http1.1 -sS -X POST \ -H "Authorization: token $TOKEN" \ -H "Accept: application/vnd.github+json" \ -d '{"body":"<reply>"}' \ https://api.github.com/repos/<owner>/<repo>/issues/<id>/comments ``` ### Technical Analysis The documented publication commands interpolate the generated `<reply>` directly into shell-quoted arguments and manually constructed JSON. The reply is derived from GitHub issue titles, bodies, and comments, which are attacker-controlled inputs. In the `gh api` command, an apostrophe in the reply can terminate the single-quoted shell argument. If the agent implements the example through textual substitution and passes the result to a shell, subsequent attacker-controlled characters may be interpreted as shell syntax. In the `curl` fallback, the reply is inserted into a manually assembled JSON string without JSON encoding. Double quotes, backslashes, control characters, or newlines can invalidate or alter the request body. Shell quoting can also be disrupted depending on how placeholders are substituted. The Skill includes an explicit publication confirmation gate, which reduces exposure but does not eliminate the vulnerability: approval authorizes publication of the visible reply, not execution of shell syntax embedded in it. ### Attack Path 1. An attacker creates or comments on an Apollo GitHub issue using text containing crafted quotation marks and shell metacharacters. 2. The Skill reads that untrusted issue content and incorporates some of it into the proposed maintainer reply. 3. A maintainer reviews the rendered reply and explicitly approves publication. 4. The agent replaces `<reply>` in one of th ...[truncated 1149 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not interpolate generated replies into shell command strings. Invoke `gh` or `curl` through a structured process API with a separate argument array and without `shell=true`. 2. Encode request bodies with a real JSON serializer. For example: ```bash payload_file=$(mktemp) trap 'rm -f "$payload_file"' EXIT jq -n --arg body "$reply" '{body: $body}' > "$payload_file" gh api "repos/$owner/$repo/issues/$issue_id/comments" \ --method POST \ --input "$payload_file" ``` 3. If `curl` is required, pass only serializer-produced JSON: ```bash payload=$(jq -n --arg body "$reply" '{body: $body}') curl --http1.1 -sS -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "Content-Type: application/json" \ --data-binary "$payload" \ "https://api.github.com/repos/$owner/$repo/issues/$issue_id/comments" ``` 4. Validate repository coordinates before use: - Owner and repository names must match an allowlisted GitHub identifier pattern. - The issue ID must contain digits only. - Prefer restricting publication to the repository supplied through trusted configuration. 5. Treat issue titles, bodies, comments, and generated replies explicitly as untrusted data. Never evaluate them as commands, templates, or instructions. 6. Keep the existing explicit publication gate, and additionally show the final repository, issue number, and exact rendered comment before execution. 7. Avoid exposing the token as an ordinary shell variable when possible. Prefer authenticated `gh api` execution so token handling remains within the GitHub CLI. ]]>
