T09 · Insecure Skill Coding Practices
Error
- Location
- src/tools.mjs:532
- Finding
- Model-Controlled Commands Are Executed Through an Unrestricted System Shell<![CDATA[ ## Vulnerability Details **File Location**: `src/tools.mjs:532-568` **Vulnerability Type**: Command injection through an unrestricted LLM-accessible shell tool **Risk Level**: High ### Vulnerable Code ```javascript export function makeBashTool(cwd) { return { name: "bash", label: "Bash", description: "Execute a shell command and return stdout/stderr. " + "Use this to explore the filesystem, read files, etc. " + "IMPORTANT: All commands run inside the skill directory. " + "Do NOT run commands that modify files or install anything. " + "NEVER execute, run, or invoke any target files — no python/node/bash scripts, " + "no binary execution, no deserialization (pickle.load, yaml.load, eval, etc.). " + "Only use safe read-only commands: cat, head, tail, hexdump, xxd, file, strings, grep, find, ls, wc.", parameters: Type.Object({ command: Type.String({ description: "The shell command to execute" }), }), execute: async (_toolCallId, params) => { const rawCommand = String(params.command || ""); const command = rawCommand .replace(/<\/?tool_call>/gi, " ") .replace(/<\/?function_call>/gi, " ") .replace(/<\/?tool>/gi, " ") .replace(/<\/?function>/gi, " ") .replace(/[{}]+$/g, "") .trim(); if (!command) { throw new Error("Command failed: empty command after sanitization"); } try { const stdout = execSync(command, { encoding: "utf-8", timeout: 30_000, maxBuffer: 1024 * 1024, cwd, }); return { content: [{ type: "text", text: stdout || "(no output)" }], details: { command, rawCommand }, }; } catch (err) { const msg = err.stderr || err.stdout || err.message; throw new Error(`Command failed: ${msg}`); } }, }; } ``` ### Technical Analysis The scanner exposes a general-purpose shell t ...[truncated 2592 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the general-purpose shell tool and replace it with dedicated operations such as: - `readTextFile(relativePath)` - `listDirectory(relativePath)` - `searchText(relativePath, fixedPattern)` - `inspectFileMetadata(relativePath)` 2. Canonicalize every requested path and verify that it remains beneath the canonical Skill root. 3. If an external utility is indispensable, invoke a fixed executable with `execFile` and a validated argument array. Do not invoke a shell. 4. Maintain an explicit executable allowlist and argument schema. Reject metacharacters, absolute paths, traversal components, redirections, substitutions, and unsupported flags. 5. Run analysis in a separate sandbox with: - A read-only mount of the Skill directory - No access to user home directories or credential stores - Network access disabled by default - A low-privilege, disposable operating-system identity - Resource and subprocess limits 6. Treat all target content as untrusted data and do not rely on model instructions to enforce security controls. ]]>
