T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/install.sh:42
- Finding
- Arbitrary Command Execution Through Shell Command Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:42-83` **Vulnerability Type**: Shell command injection in the generated OpenClaw extension **Risk Level**: Critical ### Vulnerable Code ```typescript function runCli(args: string): string { try { const env = { ...process.env, KANNAKA_DATA_DIR: DATA_DIR }; const result = execSync(`"${BINARY}" ${args}`, { timeout: 600000, encoding: "utf-8", cwd: DATA_DIR, env }); return result.trim(); } catch (err: any) { const stdout = err.stdout?.trim() || ""; if (stdout) return stdout; throw new Error(err.stderr?.trim() || err.message); } } ``` Attacker-controlled tool parameters are incorporated into the command string, including: ```typescript async execute(_id: string, p: any) { const escaped = p.content.replace(/"/g, '\\"').replace(/\n/g, ' '); const args = [`remember "${escaped}"`]; if (p.importance) args.push(`--importance ${p.importance}`); if (p.category) args.push(`--category ${p.category}`); if (p.tags?.length) args.push(`--tags "${p.tags.join(",")}"`); const text = runCli(args.join(" ")); return { content: [{ type: "text", text: `Stored memory with ID: ${text}` }] }; } ``` ```typescript async execute(_id: string, p: any) { const text = runCli( `recall "${p.query.replace(/"/g, '\\"')}" --limit ${p.limit || 5}` ); } ``` ```typescript async execute(_id: string, p: any) { return { content: [{ type: "text", text: runCli(`hear "${p.file_path.replace(/"/g, '\\"')}"`) }] }; } ``` Other affected parameters include memory IDs, relation types, dream modes, agent IDs, display names, numeric options, categories, and tags. ### Technical Analysis The generated extension passes a dynamically constructed string to Node.js `execSync`. By default, `execSync` executes that string through a system shell. Consequently, shell syntax present in any interpolated tool parameter is interpreted rather than passed to the Kannak ...[truncated 2332 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace shell-based execution with an API that accepts an argument vector: ```typescript import { execFileSync } from "child_process"; function runCli(args: string[]): string { const env = { ...process.env, KANNAKA_DATA_DIR: DATA_DIR }; return execFileSync(BINARY, args, { timeout: 600000, encoding: "utf-8", cwd: DATA_DIR, env }).trim(); } ``` 2. Pass every CLI argument as a distinct array element: ```typescript runCli(["remember", p.content, "--importance", String(p.importance)]); runCli(["recall", p.query, "--limit", String(p.limit ?? 5)]); runCli(["hear", p.file_path]); ``` 3. Do not attempt to solve shell injection through manual escaping. Avoid invoking a shell entirely. 4. Strengthen the input schemas: - Restrict importance and boost values to the documented range of `0.0` through `1.0`. - Restrict result limits to a reasonable positive integer range. - Define dream mode as an enumeration containing only `lite` and `deep`. - Constrain identifiers and relation types to explicitly supported character sets and lengths. - Set maximum lengths and item counts for content, tags, paths, and display names. 5. Where possible, validate memory identifiers using the exact identifier format produced by Kannaka. 6. Add automated security tests using payloads containing `$()`, backticks, semicolons, pipes, redirections, quotes, newlines, and whitespace. Verify that these values are delivered literally to the binary and never interpreted by a shell. 7. Run the extension under a least-privileged account with restricted filesystem and network access to reduce impact if another command-execution defect is introduced. ]]>
