T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/monitor.cjs:220
- Finding
- Shell Command Injection Through Untrusted Cron Job Metadata## Vulnerability Details **File Location**: `scripts/monitor.cjs`, lines 220–231 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js const cmd = `openclaw cron edit ${job.id} --timeout-seconds ${params.to}`; execSync(cmd, { encoding: 'utf8' }); ``` ```js const cmd = `openclaw cron edit ${job.id} --channel ${params.channel} --to "${job.delivery?.to || 'last'}"`; execSync(cmd, { encoding: 'utf8' }); ``` ### Technical Analysis The monitor reads `job.id` and `job.delivery.to` from the external OpenClaw `jobs.json` file and directly interpolates these values into commands passed to `execSync()`. Node.js executes string commands through a shell, so shell metacharacters contained in these fields can alter the intended command. Placing `job.delivery.to` inside double quotes does not make it safe. An attacker can potentially use embedded quotes, command substitution, or other shell syntax to escape the intended argument. `job.id` is not quoted at all. Exploitation requires the attacker to create or modify a cron-job record, or otherwise influence the metadata consumed by the monitor. The affected record must report a failure matching an automatic-repair rule so that one of the vulnerable methods is invoked. ### Attack Path 1. An attacker gains the ability to create or modify an entry in the OpenClaw cron `jobs.json` file. 2. The attacker inserts shell syntax into `job.id` or `job.delivery.to`. 3. The attacker configures the job state with an error message such as `job execution timed out` or `Channel is required`. 4. The monitor identifies the job as failed and selects the corresponding automatic repair. 5. The malicious field is interpolated into a command string. 6. `execSync()` invokes the shell, which interprets the injected syntax and executes the attacker's command. ### Impact Assessment Successful exploitation permits arbitrary command execution with the operating- ...[truncated 421 chars]
- Remediation
- ## Remediation Suggestions - Replace shell-based `execSync()` calls with `execFileSync()` or `spawnSync()` and pass each argument separately: ```js const { execFileSync } = require('child_process'); execFileSync('openclaw', [ 'cron', 'edit', String(job.id), '--timeout-seconds', String(params.to) ], { encoding: 'utf8', shell: false }); ``` - Apply the same argument-array approach to delivery configuration: ```js execFileSync('openclaw', [ 'cron', 'edit', String(job.id), '--channel', String(params.channel), '--to', String(job.delivery?.to || 'last') ], { encoding: 'utf8', shell: false }); ``` - Validate `job.id` against the exact identifier format accepted by OpenClaw. Reject whitespace, control characters, shell metacharacters, and malformed identifiers. - Validate channels and delivery destinations against explicit allowlists. - Treat the cron jobs file as security-sensitive input and restrict its ownership and write permissions. - Run the monitor under a dedicated, least-privileged operating-system account. - Add tests using malicious values containing semicolons, quotes, command substitutions, newlines, and option-like prefixes.
