T09 · Insecure Skill Coding Practices
Error
- Location
- to-do.js:20
- Finding
- Shell Command Injection in Task Scheduling<![CDATA[ ## Vulnerability Details **File Location**: `to-do.js`, lines 20-24 and 127-149 **Vulnerability Type**: OS command injection through dynamically constructed scheduler commands **Risk Level**: High ### Vulnerable Code ```js function execute(command) { return new Promise((resolve) => { exec(command, (error, stdout, stderr) => { resolve({ error, stdout: stdout.trim(), stderr: stderr.trim() }); }); }); } ``` ```js const taskId = `OpenClaw_Task_${Date.now()}`; const flatInstruction = instruction.replace(/\n/g, ' - '); // Quote the binary path in case it contains spaces const winCmd = `schtasks /create /tn "${taskId}" /tr "\\"${OPENCLAW_BIN}\\" agent --message \\\\"${flatInstruction}\\\\" --to \\\\"${userId}\\\\" --channel \\\\"${channel}\\\\" --deliver" /sc ONCE /st ${serverLocal.time} /sd ${serverLocal.date} /f`; const res = await execute(winCmd); ``` ```js const safeInstruction = instruction.replace(/'/g, "'\\''"); const agentCommand = `${OPENCLAW_BIN} agent --message '${safeInstruction}' --to '${userId}' --channel '${channel}' --deliver`; const atCmd = `echo "${agentCommand} >> /tmp/to-do.log 2>&1" | TZ="${tz}" at "${formattedTime}"`; const res = await execute(atCmd); ``` ### Technical Analysis The implementation passes dynamically assembled strings to Node.js `child_process.exec()`, which invokes a command shell. Several values incorporated into these commands are externally controlled or environment-controlled: - Scheduled instruction text - User ID - Channel - Time and timezone - `OPENCLAW_BIN` On Unix, escaping only single quotes in the instruction does not protect the outer double-quoted `echo` argument. Shell expansions such as command substitution using `$()` or backticks can still be interpreted by the shell. The timezone, time, routing fields, and executable path are also interpolated without strict validation. On Windows, backslash-based quote construction does not establish a reliable security bo ...[truncated 1619 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace `exec()` with `execFile()` or `spawn()` and pass every argument through an argument array with `shell: false`. 2. On Unix, start `at` directly and provide the scheduled script through the child process's standard input. Do not use an `echo | at` shell pipeline. 3. Invoke `schtasks.exe` directly on Windows with a separate argument array. 4. Validate `userId` and `channel` against narrow allowlists suitable for their expected formats. 5. Verify timezone values using `Intl.DateTimeFormat` and reject invalid identifiers before command execution. 6. Require strict `YYYY-MM-DD HH:mm` input on every platform rather than forwarding arbitrary text to `at`. 7. Require `OPENCLAW_BIN` to be an absolute path, resolve it canonically, verify it is an expected executable, and reject shell control characters. 8. Treat instruction text as opaque data rather than command text. If necessary, store the payload in a permission-restricted file and pass only the safe file path or identifier to the scheduled process. 9. Run the Skill as an unprivileged, dedicated operating-system account. ]]>
