T09 · Insecure Skill Coding Practices
Error
- Location
- pctx-skill.sh:213
- Finding
- Arbitrary Local Code Execution Through Unsafe CLI Argument Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `pctx-skill.sh`, lines 213–255 **Vulnerability Type**: Python source injection through untrusted CLI arguments **Risk Level**: High ### Vulnerable Code ```bash # Resolve namespace capitalisation local ns case "$server" in linear) ns="Linear" ;; github) ns="Github" ;; *) ns="$server" ;; esac info "Testing pctx Code Mode for '$ns'..." if [[ -z "$fn" ]]; then local list_resp list_resp=$(curl -sf --max-time 15 -X POST "http://${PCTX_HOST}:${PCTX_PORT}/mcp" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_functions","arguments":{"query":"*","limit":100}}}' \ 2>/dev/null | grep "^data:" | head -1 | sed 's/^data: //') echo "📚 Available functions in $ns:" echo "$list_resp" | python3 -c " import sys, json, re ns = '$ns' d = json.load(sys.stdin) text = d.get('result',{}).get('content',[{}])[0].get('text','') pattern = f'namespace {ns}' idx = text.find(pattern) ... " 2>/dev/null return fi local code="async function run() { const result = await ${ns}.${fn}({}); return JSON.stringify(result, null, 2); }" local call_payload call_payload=$(python3 -c " import json print(json.dumps({'jsonrpc':'2.0','id':2,'method':'tools/call','params':{ 'name':'execute_typescript', 'arguments':{'code':'$code'} }})) " 2>/dev/null) ``` ### Technical Analysis The `test` command accepts `server` and `fn` as command-line arguments. Values other than the two recognized server names are assigned directly to `ns` without validation: ```bash *) ns="$server" ;; ``` The resulting value is embedded inside Python source supplied to `python3 -c`: ```python ns = '$ns' ``` Both `ns` and `fn` are also included in `code`, which is subsequently embedded inside another single-quoted Python string: ```python 'arguments':{'code':'$code'} ``` Shell quoting does not make these generated Python strings ...[truncated 1898 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Never interpolate command-line values into Python program text. Pass values as positional arguments: ```bash python3 - "$ns" "$code" <<'PY' import json import sys namespace = sys.argv[1] code = sys.argv[2] print(json.dumps({ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "execute_typescript", "arguments": {"code": code}, }, })) PY ``` 2. Validate namespace and function names before using them: ```bash [[ "$ns" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || die "Invalid server namespace" [[ "$fn" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || die "Invalid function name" ``` 3. Prefer a fixed allowlist of configured MCP namespaces rather than accepting arbitrary namespace expressions. 4. Construct JSON only with a serializer. Do not manually combine JSON, Python, or TypeScript source through nested string interpolation. 5. Add negative tests covering single quotes, double quotes, newlines, backslashes, semicolons, Unicode control characters, and shell metacharacters in both arguments. ]]>
