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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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)

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/schedule.py (reported line 166)May include surrounding context.

python
"""更新 crontab"""
    lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 266)May include surrounding context.

md
lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",
        "SHELL=/bin/bash",

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/schedule.py (reported line 167)May include surrounding context.

python
lines = [
        "# OpenClaw 定时任务",
        "# 编辑:crontab -e",
        "# 查看:crontab -l",
        "",
        "# 环境变量",
        "SHELL=/bin/bash",

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · scripts/schedule.py (reported line 198)May include surrounding context.

python
# 使用 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:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/schedule.py (reported line 244)May include surrounding context.

python
# 显示 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} 个启用的任务")

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · scripts/schedule.py (reported line 461)May include surrounding context.

python
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}")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
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.

Content

Scanner excerpt · package.json (reported line 32)May include surrounding context.

json
"test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "edge-tts": "^6.1.0"
  },
  "engines": {
    "node": ">=18.0.0",

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.