T09 · Insecure Skill Coding Practices
Error
- Location
- _scripts/archive.mjs:163
- Finding
- Shell Command Injection in AI Classification<![CDATA[ ## Vulnerability Details **File Location**: `_scripts/archive.mjs`, lines 163-186 and 248-250 **Vulnerability Type**: OS command injection through shell-interpolated document data and environment configuration **Risk Level**: High ### Vulnerable Code ```javascript async function aiClassify(filename, text, summary) { const prompt = `你是一个文件分类助手。请根据以下文件信息判断它应该归类到哪个分类。 文件名: ${filename} 内容摘要: ${summary.substring(0, 500)} 内容片段: ${text.substring(0, AI_CLASSIFY_CONFIG.maxTextLength)} 可选分类: 1. 工作文件 - 数据报表、销售业绩、门店运营、统计分析等 2. 方案文档 - 计划方案、策略规划、制度流程、管理规范等 3. 参考资料 - 话术模板、培训教程、案例经验、指南手册等 4. 其他文档 - 不属于以上分类的文档 请只输出分类名称(工作文件/方案文档/参考资料/其他文档),不要其他内容。`; try { let result; try { result = execSync(`openclaw chat --prompt "${escapeShell(prompt)}" --model ${AI_CLASSIFY_CONFIG.model}`, { encoding: 'utf-8', timeout: 30000, }).trim(); } catch (e) { // Fallback behavior omitted } ``` ```javascript function escapeShell(str) { return str.replace(/"/g, '\\"').replace(/\n/g, ' ').replace(/\r/g, ''); } ``` ### Technical Analysis The implementation constructs a shell command by interpolating the filename, extracted document content, summary, and `OPENCLAW_MODEL` environment variable into a string passed to `execSync()`. The `escapeShell()` function only escapes double quotes and removes line breaks. It does not prevent shell expansion constructs such as: - `$(command)` - Backtick command substitution - Shell metacharacters supplied through the unquoted model value - Other shell expansions interpreted inside double-quoted strings Because `prompt` incorporates attacker-controlled filenames and extracted document text, opening a crafted file with `--ai-classify` can cause the shell to evaluate command substitutions before `openclaw` receives the prompt. The `AI_CLASSIFY_CONFIG.model` value is not quoted or validated at all, allowing direct command injection if the environment variable is attacker-controlled. ### Attack Pa ...[truncated 1151 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace shell-string execution with an argument-based API: ```javascript execFileSync('openclaw', [ 'chat', '--prompt', prompt, '--model', AI_CLASSIFY_CONFIG.model, ], { encoding: 'utf-8', timeout: 30000, shell: false, }); ``` 2. Validate model identifiers using a restrictive allowlist, for example: ```javascript if (!/^[A-Za-z0-9._:/-]+$/.test(AI_CLASSIFY_CONFIG.model)) { throw new Error('Invalid model identifier'); } ``` 3. Never rely on custom shell escaping for untrusted document content. 4. Treat filenames and extracted text as hostile data regardless of file origin. 5. Add regression tests using filenames and content containing `$()`, backticks, quotes, semicolons, and control characters. 6. Run optional model integrations in a restricted process with minimum filesystem and network privileges. ]]>
