T09 · Insecure Skill Coding Practices
Error
- Location
- index.js:88
- Finding
- Shell Command Injection Through Untrusted Cron Job Identifier<![CDATA[ ## Vulnerability Details **File Location**: `index.js`, lines 88-90 **Vulnerability Type**: OS command injection through shell interpolation **Risk Level**: High ### Vulnerable Code ```js if (shouldRemove) { try { console.log(`[CronOptimizer] Removing stale job: ${job.name} (ID: ${job.id}, Age: ${Math.floor(age/3600000)}h)`); execSync(`openclaw cron remove "${job.id}"`, { stdio: 'ignore' }); removedCount++; } catch (e) { console.error(`[CronOptimizer] Failed to remove job ${job.id}:`, e.message); } } ``` ### Technical Analysis The cron job identifier, `job.id`, is interpolated directly into a command string passed to `child_process.execSync()`. Because `execSync()` executes string commands through a shell, shell metacharacters contained in the identifier are interpreted by the shell. Wrapping the value in double quotes is insufficient. Shell features such as command substitution remain active inside double quotes, and a value containing a double quote can terminate the quoted argument and append new commands. The identifier originates from either: - The JSON output of `openclaw cron list --all --json`; or - The locally stored `evolver_cron_cache.json` cache. Neither source is structurally validated before the identifier reaches the command execution sink. ### Attack Path 1. An attacker influences the output of the OpenClaw cron listing or modifies the cache file used by the optimizer. 2. The attacker inserts a job whose identifier contains a shell payload. 3. The malicious job is configured to satisfy one of the removal conditions, such as being disabled and old enough. 4. The optimizer interpolates the malicious identifier into: ```sh openclaw cron remove "<attacker-controlled value>" ``` 5. The shell interprets the embedded payload and executes it with the privileges of the Node.js process. ### Impact Assessment Successful exploitation permits arbitrary command execution with the operat ...[truncated 378 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid invoking a shell for commands containing externally derived values. Replace the string-based call with an argument-array API: ```js const { execFileSync } = require('child_process'); execFileSync( 'openclaw', ['cron', 'remove', String(job.id)], { stdio: 'ignore' } ); ``` Additionally: 1. Validate that `job.id` is a string and conforms to the exact documented OpenClaw identifier format. 2. Reject identifiers containing whitespace, control characters, quotes, or shell metacharacters. 3. Validate the full structure and types of cached cron records before using them. 4. Protect the cache file against unauthorized modification. 5. Apply the same no-shell rule to future commands that incorporate variable data. ]]>
