T09 · Insecure Skill Coding Practices
- Location
src/scheduler.js:48- Finding
Shell and Cron Injection Through Unsanitized Schedule Rule IDs
- Content
View full analysis
Vulnerability Details
File Location:
src/scheduler.js:48-50, 212-214
Vulnerability Type: Command injection in generated cron entries
Risk Level: MediumThe scheduler validates a rule ID only as a non-empty string:
js if (!rule.id || typeof rule.id !== 'string') { throw new Error('rule.id must be a non-empty string'); }It later inserts that value directly into shell commands without validation or quoting:
js lines.push(`# ${rule.id} -> ${rule.model}`); lines.push(`${startM} ${startH} * * ${rule.days.map(d => DAYS.indexOf(d)).join(',')} node src/cli.js schedule apply --id ${rule.id}`); lines.push(`${endM} ${endH} * * ${rule.days.map(d => DAYS.indexOf(d)).join(',')} node src/cli.js schedule end --id ${rule.id}`);Technical Analysis
The
schedule addcommand accepts and persists any non-empty rule ID. Whenschedule cronis subsequently invoked,buildCronPreview()interpolates the ID directly into generated crontab command lines.Because the value is neither restricted to a safe identifier grammar nor encoded as a shell argument, an attacker-controlled ID may contain shell metacharacters, command substitutions, whitespace, or newline characters. This can alter the syntax of the generated crontab and append arbitrary commands.
The generated cron output is documented as requiring manual installation, so generating the preview alone does not execute the payload. Exploitation occurs if a user or automated deployment process trusts and installs the generated output.
Attack Path
- An attacker gains the ability to invoke
schedule add, influence its arguments, or modify a schedule file consumed by the application. - The attacker supplies a malicious rule ID containing shell syntax or a newline followed by an additional cron entry.
- The application accepts the ID because validation checks only that it is a non-empty string.
- The attacker-controlled value is persist ...[truncated 1047 chars]
- An attacker gains the ability to invoke
- Remediation
View remediation
Remediation Suggestions
-
Enforce a strict allowlist for rule IDs at every input and configuration boundary. For example:
js const SAFE_RULE_ID = /^[A-Za-z0-9_-]+$/; if ( typeof rule.id !== 'string' || !SAFE_RULE_ID.test(rule.id) ) { throw new Error( 'rule.id may contain only letters, digits, underscores, and hyphens' ); } -
Explicitly reject control characters, carriage returns, newlines, whitespace, quotes, backticks, dollar signs, semicolons, pipes, redirection operators, and other shell metacharacters.
-
Do not construct cron shell commands by raw string interpolation. Apply a well-reviewed POSIX shell-argument quoting function to every dynamic argument, even after allowlist validation.
-
Validate existing schedule files when loading them so that malicious IDs introduced by direct file modification cannot bypass CLI validation.
-
Generate cron entries from structured, trusted data and clearly separate comments from executable lines. Avoid inserting untrusted content into comments because newline characters can escape the comment context.
-
Add regression tests covering IDs containing semicolons, pipes, command substitutions, backticks, quotes, spaces, tabs, carriage returns, and newline-injected cron entries.
-
Document that cron previews must not be installed without validation, and ensure automated deployment processes reject generated output containing unexpected additional lines or commands.
-
