T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/setup_cron.py:25
- Finding
- Cron Command Injection Through an Unquoted Workspace Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_cron.py:25-33, 40, 56-58` **Vulnerability Type**: Shell command injection through generated cron entries **Risk Level**: High ### Vulnerable Code ```python def default_jobs(workspace: Path) -> list[str]: py = "python3" base = workspace / "skills" / "learning-coach" / "scripts" weekly = base / "weekly_report.py" return [ f"30 7 * * * {py} {weekly} --mode daily-morning {TAG}", f"0 20 * * * {py} {weekly} --mode daily-evening {TAG}", f"0 10 * * 3,6 {py} {weekly} --mode curation-refresh {TAG}", f"0 7 * * 1 {py} {weekly} --mode weekly {TAG}", ] def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("action", choices=["apply", "remove", "show"]) ap.add_argument("--workspace", default=str(Path.home() / ".openclaw" / "workspace")) args = ap.parse_args() # ... jobs = default_jobs(Path(args.workspace)) merged = filtered + jobs set_crontab("\n".join([x for x in merged if x.strip()]) + "\n") ``` ### Technical Analysis The user-controlled `--workspace` value is interpolated directly into cron command strings without shell quoting or validation. Although the script does not invoke these command strings immediately, cron later executes each command through a shell. `pathlib.Path` does not neutralize shell metacharacters such as semicolons, command substitutions, redirection operators, or comment characters. Consequently, a crafted workspace value can change the structure of the scheduled shell command. The script also modifies the user's complete crontab through `crontab -`, making the injected command persistent across sessions until the entry is removed. ### Attack Path 1. An attacker causes the script to be invoked with a malicious workspace value, for example: ```bash python3 scripts/setup_cron.py apply \ --workspace '/tmp/fake; touch /tmp/learning-coach-pwned #' ``` 2. `default_jobs()` e ...[truncated 867 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve the workspace and require it to remain under an explicitly approved root: ```python approved_root = (Path.home() / ".openclaw" / "workspace").resolve() workspace = Path(args.workspace).expanduser().resolve() if workspace != approved_root: raise SystemExit("Unapproved workspace path") ``` 2. Quote every executable and path placed in a cron shell command: ```python import shlex command = ( f"{shlex.quote(sys.executable)} " f"{shlex.quote(str(weekly))} " "--mode daily-morning" ) ``` 3. Reject newline characters and other control characters in every value included in a crontab entry. 4. Prefer generating a fixed wrapper script at a trusted path and scheduling only that fixed script. 5. Before applying jobs, validate `data/cron-consent.json` and confirm that the exact commands and schedules match the user's approval. 6. Display the final escaped cron entries and require explicit confirmation before installation. ]]>
