T09 · Insecure Skill Coding Practices
Error
- Location
- opencode-session.sh:344
- Finding
- Unescaped User Input in Agent-Executable Workflow Output## Vulnerability Details **File Location**: `opencode-session.sh`, lines 344-358 **Vulnerability Type**: Command and JSON-RPC instruction injection **Risk Level**: High ### Vulnerable Code ```bash ## Step 1: Start OpenCode exec(command: "opencode acp --cwd $project", background: true, workdir: "$project") # → Save returned sessionId as PROCESS_SESSION_ID ## Step 2: Initialize process.write(PROCESS_SESSION_ID, data: '$(initialize_opencode)' + "\\n") process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_INIT) # → Expect: {"result":{"protocolVersion":1,...}} ## Step 3: Create Session process.write(PROCESS_SESSION_ID, data: '$(create_session "$project" "$mcp_servers")' + "\\n") process.poll(PROCESS_SESSION_ID, timeout: $TIMEOUT_SESSION) # → Save result.sessionId as OPENCODE_SESSION_ID ## Step 4: Send Prompt process.write(PROCESS_SESSION_ID, data: '$(send_prompt "OPENCODE_SESSION_ID" "$prompt")' + "\\n") ``` The underlying JSON-RPC construction also directly interpolates these values: ```bash create_session() { local cwd="$1" local mcp_servers="$2" log INFO "Creating session in $cwd" local params="{\"cwd\":\"${cwd}\",\"mcpServers\":${mcp_servers:-[]}}" local json=$(send_jsonrpc "session/new" "$params") echo "$json" } send_prompt() { local session_id="$1" local prompt="$2" log INFO "Sending prompt (${#prompt} chars)" local params="{\"sessionId\":\"${session_id}\",\"prompt\":[{\"type\":\"text\",\"text\":\"${prompt}\"}]}" local json=$(send_jsonrpc "session/prompt" "$params") echo "$json" } ``` ### Technical Analysis Values supplied through `--project`, `--prompt`, and `--mcp` are inserted into generated command syntax and JSON-RPC messages without context-appropriate escaping or validation. The script does not directly execute the generated workflow. However, its documentation explicitly instructs CYPHER or another consuming age ...[truncated 1919 chars]
- Remediation
- ## Remediation Suggestions 1. Stop producing executable command syntax through string interpolation. Return a structured data document whose fields remain data rather than instructions. 2. Construct all JSON-RPC messages with a real JSON serializer, such as: ```bash jq -cn \ --arg cwd "$cwd" \ --argjson mcpServers "$mcp_servers" \ '{jsonrpc:"2.0", id:1, method:"session/new", params:{cwd:$cwd, mcpServers:$mcpServers}}' ``` 3. Construct prompt messages with `jq --arg prompt "$prompt"` so quotation marks, newlines, backslashes, and control characters are escaped correctly. 4. Validate MCP input before use: ```bash jq -e 'type == "array" and all(.[]; type == "string")' \ <<< "$mcp_servers" >/dev/null ``` 5. Canonicalize the project path with `realpath`, require it to be an existing directory, and optionally restrict it to approved workspace roots. 6. Invoke processes through structured argument arrays rather than a generated shell command string. 7. Ensure the downstream agent treats script output as untrusted data. It should execute only a fixed, locally defined workflow and populate validated parameters into that workflow. 8. Add regression tests covering embedded quotation marks, newlines, backslashes, JSON delimiters, and strings resembling agent tool calls.
