Back to skill

Security audit

Schedule Manager

Security checks for vulnerabilities and agentic risk

Overview

This reminder skill has a coherent purpose, but it rewrites the user's crontab and builds persistent cron commands from weakly validated task fields.

Review this skill carefully before installing. Do not run it from a privileged account, back up your current crontab first, and avoid adding tasks whose names, weekdays, or times come from untrusted text until the cron update logic preserves existing entries and validates or safely quotes all fields.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T06 · System Persistence

Error
Location
scripts/schedule.py:162
Finding
User crontab is replaced instead of safely updating Skill-owned entries## Vulnerability Details **File Location**: `scripts/schedule.py`, lines 162–210 **Vulnerability Type**: Destructive scheduled-task persistence management **Risk Level**: High ### Vulnerable Code ```python def update_crontab(tasks): lines = [ "# OpenClaw scheduled tasks", "# Edit: crontab -e", "# View: crontab -l", "", "SHELL=/bin/bash", "PATH=/usr/local/bin:/usr/bin:/bin:/home/wang/.npm-global/bin", "", "# Daily scheduled tasks" ] for task in tasks: if task.get('status') == '✅': time_parts = task['time'].split(':') hour = time_parts[0] minute = time_parts[1] weekday = task.get('weekday', '1-7') if weekday == '1-5': day_of_week = "1-5" elif weekday == '1-7' or weekday == '*': day_of_week = "*" else: day_of_week = weekday lines.append(f"{minute} {hour} * * {day_of_week} {RUN_TASK_SCRIPT} \"{task['name']}\"") crontab_content = '\n'.join(lines) + '\n' try: proc = subprocess.Popen( ['crontab', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = proc.communicate(crontab_content.encode()) ``` ### Technical Analysis The `crontab -` command installs its standard input as the complete crontab of the current operating-system user. The function constructs that input solely from tasks managed by this Skill. It does not read the existing crontab, preserve unrelated entries, or delimit Skill-owned records with managed-section markers. Consequently, every operation that calls `update_crontab()`—including adding, editing, enabling, disabling, toggling, or deleting a task—replaces the user's entire crontab. Scheduling persistence is necessar ...[truncated 1266 chars]
Remediation
## Remediation Suggestions 1. Read the existing crontab with `crontab -l` before making changes. 2. Manage only a clearly delimited section, for example: ```text # BEGIN OPENCLAW SCHEDULE-MANAGER ... # END OPENCLAW SCHEDULE-MANAGER ``` 3. Replace only the content inside those markers and preserve every unrelated line byte-for-byte. 4. Back up the original crontab before installation and restore it if installation fails. 5. Show the proposed changes and require explicit user confirmation before the first cron modification. 6. Provide an uninstall or cleanup command that removes only the managed section. 7. Prefer a single fixed dispatcher entry rather than installing one cron record per user task. 8. Never require elevated privileges; modify only the invoking user's crontab.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/schedule.py:180
Finding
Unvalidated task fields allow persistent cron command injection## Vulnerability Details **File Location**: `scripts/schedule.py`, lines 180–207 **Vulnerability Type**: Persistent command injection through generated crontab syntax **Risk Level**: High ### Vulnerable Code ```python for task in tasks: if task.get('status') == '✅': time_parts = task['time'].split(':') hour = time_parts[0] minute = time_parts[1] weekday = task.get('weekday', '1-7') if weekday == '1-5': day_of_week = "1-5" elif weekday == '1-7' or weekday == '*': day_of_week = "*" else: day_of_week = weekday lines.append( f"{minute} {hour} * * {day_of_week} " f"{RUN_TASK_SCRIPT} \"{task['name']}\"" ) crontab_content = '\n'.join(lines) + '\n' proc = subprocess.Popen( ['crontab', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = proc.communicate(crontab_content.encode()) ``` The relevant command-line fields are accepted without strict validation: ```python add_parser.add_argument('--time', required=True, help='Execution time (HH:MM)') add_parser.add_argument('--name', required=True, help='Task name') add_parser.add_argument('--weekday', default='1-7', help='Weekday specification') ``` ### Technical Analysis The `time`, `weekday`, and `name` fields are incorporated into crontab syntax without strict parsing, control-character rejection, or shell-safe serialization. The task name is surrounded by double quotes, but double quotes do not neutralize shell command substitution. Metacharacters such as `$()` remain active when cron invokes `/bin/bash`. Quotes, semicolons, backticks, backslashes, and newline characters can also change the generated command or inject additional cron records. The time and weekday fields are even more directly exposed: they are inserted into cron scheduling f ...[truncated 1853 chars]
Remediation
## Remediation Suggestions 1. Parse `--time` with `datetime.strptime(value, "%H:%M")` and reject values outside valid hour and minute ranges. 2. Parse weekdays against a strict allowlist or a dedicated cron-field parser. Reject whitespace, control characters, and unsupported expressions. 3. Reject `\r`, `\n`, null bytes, quotes, backticks, dollar signs, semicolons, pipes, redirection operators, and other shell metacharacters in identifiers used by cron. 4. Do not place human-readable task names in shell commands. Generate an opaque identifier with a restricted format such as `[A-Za-z0-9_-]+`. 5. Prefer one constant cron entry that invokes a fixed dispatcher. The dispatcher should load structured task data and select tasks by validated opaque identifiers. 6. If arguments must be serialized into a shell command, use robust shell quoting such as `shlex.quote()`, while still rejecting line breaks because shell quoting alone cannot safely represent injected crontab records. 7. Validate records loaded from `daily-tasks.md`; do not trust them merely because they are stored locally. 8. Install only a marker-delimited managed section and verify the generated crontab before committing it. 9. Add automated tests covering command substitution, quotes, semicolons, backticks, newlines, malformed times, and malicious weekday expressions.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (15)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documents shell execution plus read/write access to workspace files, but it does not declare any explicit tool scope such as permissions or allowed-tools. That increases the chance an agent invokes broader capabilities than intended, especially for commands that alter cron state and task files. In this context the risk is real because the skill is specifically designed to modify persistent scheduler configuration and run shell commands.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation includes delete and cleanup operations that remove scheduled or temporary tasks, but it does not clearly warn users that these actions are destructive and persistent. In an agent setting, insufficient disclosure can lead to accidental task loss or unintended deletion when commands are executed on the user's behalf.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
This manifest uses Chinese-only natural-language description text and includes a locale-specific keyword "chinese", indicating the skill is oriented to a single language without stating that language choice is optional. The policy allows locale constraints only when explicitly documented and justified or offered as opt-in, which is not present here.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code file contains natural-language strings such as the module docstring and argument/help descriptions entirely in Chinese, which forces a specific language for users interacting with the skill. The policy allows locale constraints only when they are opt-in or clearly justified, and neither is present here.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The skill's stated purpose is schedule management, but its implementation silently installs persistent host-level cron jobs by replacing the user's crontab. In this context that is more dangerous because a seemingly simple reminder tool gains durable execution capability on the system, increasing risk of persistence, denial of service, and abuse if task contents or names are manipulated.

Session Persistence

Medium
Category
Rogue Agent
Content
"""更新 crontab"""
    lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",
        "SHELL=/bin/bash",
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",
        "SHELL=/bin/bash",
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 使用 crontab 命令
    try:
        proc = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE, 
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = proc.communicate(crontab_content.encode())
        if proc.returncode == 0:
Confidence
93% confidence
Finding
This code rewrites the user's entire crontab via subprocess, creating persistent scheduled execution on the host. Although it does not use shell=True, task data is inserted into cron lines without robust validation or escaping, so malformed task names or schedule fields can break the crontab format or potentially inject additional cron entries/commands depending on how downstream scripts consume the argument.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 显示 crontab 状态
        print("\n⏰ crontab 状态:")
        try:
            result = subprocess.run(['crontab', '-l'], capture_output=True, text=True)
            if result.returncode == 0:
                task_count = len([t for t in tasks if t.get('status') == '✅'])
                print(f"已配置 {task_count} 个启用的任务")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
A reminder-management skill invoking another skill as a subprocess creates an unexpected execution dependency and widens the attack surface. In this context, reminder text from task storage flows into a separate executable component, so compromise or unsafe behavior in the TTS skill can be triggered through this scheduler.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if '本地播放' in task.get('notify', ''):
                try:
                    tts_script = WORKSPACE / "skills" / "edge-tts" / "scripts" / "tts.py"
                    subprocess.run(['python3', str(tts_script), '--play', task['message']], 
                                 timeout=30, capture_output=True)
                except Exception as e:
                    log("ERROR", f"语音播放失败:{e}")
Confidence
84% confidence
Finding
The script executes another skill's Python program and passes user-controlled reminder text to it. Even without shell=True, this expands the trust boundary to an external script whose argument parsing, file access, or audio backend behavior may be unsafe, so untrusted reminder content can trigger downstream issues or unexpected code paths.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The natural-language description forces a specific language presentation for the skill and its usage instructions, with no indication that users can choose another language or that the skill is intentionally limited to a Chinese-speaking context. The policy explicitly calls for flagging language or locale restrictions unless there is user opt-in or clear justification.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "edge-tts": "^6.1.0"
  },
  "engines": {
    "node": ">=18.0.0",
Confidence
87% confidence
Finding
The dependency uses a caret range (^6.1.0), which allows automatic installation of newer minor and patch releases. If the upstream package is compromised or introduces a malicious or breaking change, consumers of this skill could silently receive it during install, creating a supply-chain risk.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The CLI help text explicitly says `--today` will list today's temporary tasks. In `cmd_list`, the only branch checks `args.temp`, and there is no logic that filters by today's date, so the documented intent contradicts actual behavior.

Static analysis

No suspicious patterns detected.