Back to skill

Security audit

Job Execution Monitor

Security checks for vulnerabilities and agentic risk

Overview

This cron-monitoring skill is not clearly malicious, but it installs recurring user-level automation with weak validation that could be abused or repeatedly wake the agent.

Install only if you are comfortable with a user-level systemd timer or cron job that periodically runs this skill and can wake OpenClaw. Keep the workspace path and JSON config under your control, use only numeric intervals/tolerances, and prefer patching validation and explicit install confirmation before enabling it. The cleanup commands permanently remove this skill's config, state, and logs.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install.sh:34
Finding

Unvalidated configuration is injected into persistent scheduler definitions

Content
View full analysis
"$SERVICE_FILE" < "$TIMER_FILE" </dev/null | grep -qF "$HEALTHCHECK_SCRIPT"; then echo "⚠️ Cron entry already exists" else (crontab -l 2>/dev/null; echo "$CRON_LINE") | crontab - echo "✅ Cron job added" fi ``` ### Technical Analysis `CHECK_INTERVAL` is loaded from a user-writable JSON configuration without verifying that it is an integer within a safe range. `WORKSPACE` is derived from `OPENCLAW_WORKSPACE` without rejecting newlines, quotes, whitespace, or shell metacharacters. These values are directly interpolated into systemd unit files or a crontab entry. A newline in either value can introduce additional systemd directives or cron records. In the cron fallback, shell metacharacters in the workspace value can alter the command interpreted by the cron shell. Because the generated definition is enabled and started, successful injection is persistent and executes whenever the timer or cron entry ...[truncated 1379 chars]
Remediation
View remediation
= 1 and . <= 1440) ' "$CONFIG_FILE") || { echo "ERROR: checkIntervalMin must be an integer from 1 to 1440" >&2 exit 1 } ``` 2. Canonicalize the workspace path and reject control characters, newlines, carriage returns, and NUL bytes. 3. Escape values according to systemd unit-file syntax rather than inserting raw environment values into a heredoc. 4. Avoid assembling a cron shell command from configurable text. Use a fixed wrapper script whose path is controlled by the package and pass validated configuration through a protected environment file. 5. Quote all shell path uses within the generated cron command using a robust shell-escaping mechanism. 6. Generate definitions in a temporary file with restrictive permissions, validate them, and then atomically move them into place. 7. Require explicit user confirmation before enabling the persistent timer, especially when a non-default workspace or interval is used. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/healthcheck.sh:57
Finding

Untrusted values are evaluated in Bash arithmetic expressions

Content
View full analysis
today_run + tolerance )); then ``` ```bash schedule=$(echo "$job_config" | jq -r '.schedule // ""') tolerance=$(echo "$job_config" | jq -r '.tolerance // 600') ``` ```bash alert_key="${job_name}_missing" already_alerted=$(echo "$STATE" | jq -r --arg k "$alert_key" '.alerts[$k] // 0') if (( already_alerted == 0 )); then ``` ```bash # Extract last run time (ms) and status last_run_ms=$(echo "$job_json" | jq -r '.state.lastRunAtMs // 0') last_status=$(echo "$job_json" | jq -r '.state.lastStatus // "unknown"') last_run=$((last_run_ms / 1000)) # Convert to seconds if (( last_run == 0 )); then ``` ```bash expected_time=$(parse_cron_next "$schedule" "$tolerance") time_diff=$((NOW - last_run)) expected_diff=$((NOW - expected_time)) if (( expected_diff > tolerance && time_diff > tolerance )); then ``` ### Technical Analysis Values obtained from the editable configuration, persistent state file, and `openclaw cron list` output are used as Bash arithmetic operands without first verifying their types and formats. Bash arithmetic contexts interpret operand contents as arithmetic expressions rather than strictly converting decimal strings. Crafted expressions can cause recursive variable or array-subscript evaluation; in relevant Bash evaluation contexts, ma ...[truncated 1545 chars]
Remediation
View remediation
&2 exit 1 fi } ``` 2. Enforce sensible upper and lower bounds for timestamps, tolerances, intervals, hours, and minutes. 3. Require appropriate JSON types with `jq -e`, rather than accepting strings through `jq -r`. 4. Validate `lastRunAtMs` and all OpenClaw response fields against an expected schema before using them. 5. Treat a malformed state file as corrupt: move it aside, initialize a new safe state file, and emit a diagnostic message. 6. Parse cron fields with strict patterns. For the currently supported format, require exactly five fields and numeric minute/hour values in valid ranges. 7. Use explicit base-10 conversion only after validation, such as `10#$value`, to avoid alternate-base and expression interpretation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/healthcheck.sh:76
Finding

Pipeline subshell discards alert-state updates and enables repeated wake events

Content
View full analysis
/dev/null | jq --arg name "$job_name" '.jobs[] | select(.name == $name)' 2>/dev/null || echo '{}') if [[ "$job_json" == "{}" ]]; then # Job not found alert_key="${job_name}_missing" already_alerted=$(echo "$STATE" | jq -r --arg k "$alert_key" '.alerts[$k] // 0') if (( already_alerted == 0 )); then msg="🔴 Job-Observer: Job \"$job_name\" not found in cron list" echo "$msg" send_wake "$msg" STATE=$(echo "$STATE" | jq --arg k "$alert_key" --arg t "$NOW" '.alerts[$k] = ($t | tonumber)') fi continue fi # Additional job checks omitted here; they update STATE in the same loop. done # Update state file STATE=$(echo "$STATE" | jq --arg t "$NOW" '.lastCheck = ($t | tonumber)') echo "$STATE" > "$STATE_FILE" ``` ### Technical Analysis In Bash, a `while` loop placed at the end of a pipeline normally executes in a subshell. Assignments made to `STATE` inside that subshell do not survive after the loop exits. The code therefore loses alert additions and recovery deletions performed inside the loop. The final state write only updates `lastCheck` using the parent shell's unchanged copy of `STATE`. This defeats the documented anti-spam behavior. A job that remains missing, has never run, or is considered late can cause ...[truncated 1177 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (19)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

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

systemctl --user daemon-reload

Optional: remove unit files

rm -f ~/.config/systemd/user/openclaw-job-execution-monitor.service
~/.config/systemd/user/openclaw-job-execution-monitor.timer

text

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

This command permanently deletes the skill's configuration file from the user's workspace. In a skill document, presenting destructive commands without clear warning or confirmation creates a real risk of accidental data loss, especially when users may follow instructions verbatim.

Content

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

Optional cleanup (config/state/log)

bash
rm -f ~/.openclaw/workspace/job-execution-monitor.json
rm -f ~/.openclaw/workspace/.job-execution-monitor-state.json
rm -f ~/.openclaw/workspace/job-execution-monitor.log

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Deleting the state file removes alert history and operational state, which can interfere with recovery logic or auditability. Although the file path is fixed and not attacker-supplied, the danger comes from destructive behavior being exposed without sufficient caution in an agent-facing skill.

Content

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

Optional cleanup (config/state/log)

bash
rm -f ~/.openclaw/workspace/job-execution-monitor.json
rm -f ~/.openclaw/workspace/.job-execution-monitor-state.json
rm -f ~/.openclaw/workspace/job-execution-monitor.log

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
89% confidence
Finding

Deleting the log file destroys troubleshooting and audit information that may be needed to investigate job failures or monitor behavior. In context this is not arbitrary command abuse, but it is still a meaningful destructive action presented without enough warning.

Content

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

bash
rm -f ~/.openclaw/workspace/job-execution-monitor.json
rm -f ~/.openclaw/workspace/.job-execution-monitor-state.json
rm -f ~/.openclaw/workspace/job-execution-monitor.log

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `SKILL.md` - This documentation

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The invocation example "check if jobs ran" is broad natural language that could match many ordinary troubleshooting requests outside this specific cron-monitoring skill. The document does not provide narrowing constraints or negative examples to clarify when this skill should versus should not activate.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The phrase "task surveillance" is uncommon and vague, giving little indication of what systems, job types, or monitoring context it refers to. Without explicit scope, it could cause unintended invocation for unrelated task-tracking or productivity requests.

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 · SKILL.md (reported line 61)May include surrounding context.

If installed via cron

bash
crontab -l | sed '/job-execution-monitor\/scripts\/healthcheck\.sh/d' | crontab -

Optional cleanup (config/state/log)

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/install.sh (reported line 105)May include surrounding context.

sh
### If installed via cron
```bash
crontab -l | sed '/job-execution-monitor\/scripts\/healthcheck\.sh/d' | crontab -
```

### Optional cleanup (config/state/log)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The uninstall section includes direct file-deletion commands for config, state, and logs without an explicit warning that user data and monitoring history will be permanently removed. In an agent context, users may copy or authorize these commands without understanding the data-loss consequences.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/install.sh (reported line 14)May include surrounding context.

sh
# Check if running as root
if [[ $EUID -eq 0 ]]; then
  echo "ERROR: Don't run as root. User-level systemd/cron is preferred." >&2
  exit 1
fi

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/install.sh (reported line 21)May include surrounding context.

sh
# Check dependencies
for cmd in jq; do
  if ! command -v "$cmd" &>/dev/null; then
    echo "ERROR: $cmd not found. Install with: sudo apt install $cmd" >&2
    exit 1
  fi
done

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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/install.sh (reported line 41)May include surrounding context.

sh
if command -v systemctl &>/dev/null && systemctl --user status &>/dev/null; then
  echo "✅ Using systemd user timer"
  
  # Create service file
  SERVICE_FILE="$HOME/.config/systemd/user/openclaw-job-execution-monitor.service"
  TIMER_FILE="$HOME/.config/systemd/user/openclaw-job-execution-monitor.timer"

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

Enabling a systemd user timer establishes persistence by configuring the healthcheck to run automatically in the future. In an installer for a monitoring component this is likely intentional, but persistence remains security-sensitive because any compromise of the referenced script or configuration can turn the timer into a recurring execution vector.

Content

Scanner excerpt · scripts/install.sh (reported line 79)May include surrounding context.

sh
# Reload and enable
  systemctl --user daemon-reload
  systemctl --user enable openclaw-job-execution-monitor.timer
  systemctl --user start openclaw-job-execution-monitor.timer
  
  echo "✅ Systemd timer installed and started"

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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/install.sh (reported line 82)May include surrounding context.

sh
systemctl --user enable openclaw-job-execution-monitor.timer
  systemctl --user start openclaw-job-execution-monitor.timer
  
  echo "✅ Systemd timer installed and started"
  echo ""
  echo "Commands:"
  echo "  systemctl --user status openclaw-job-execution-monitor.timer"

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

The script installs a recurring per-user cron job, which creates persistence by automatically re-running the healthcheck script. Even though this appears to be intended functionality for monitoring, persistence mechanisms are security-relevant because a compromised or later-modified healthcheck script would be executed repeatedly without further user interaction.

Content

Scanner excerpt · scripts/install.sh (reported line 95)May include surrounding context.

sh
CRON_LINE="*/${CHECK_INTERVAL} * * * * OPENCLAW_WORKSPACE=${WORKSPACE} ${HEALTHCHECK_SCRIPT} >> ${WORKSPACE}/job-execution-monitor.log 2>&1"
  
  # Check if already in crontab
  if crontab -l 2>/dev/null | grep -qF "$HEALTHCHECK_SCRIPT"; then
    echo "⚠️  Cron entry already exists"
  else

Session Persistence

Medium
Category
Rogue Agent
Confidence
94% confidence
Finding

This line appends the persistent cron entry into the user's crontab, establishing automatic execution on a schedule. In this skill's context that is expected installer behavior, but it still materially changes the user's environment and can be abused if the referenced script or workspace path is tampered with later.

Content

Scanner excerpt · scripts/install.sh (reported line 99)May include surrounding context.

sh
if crontab -l 2>/dev/null | grep -qF "$HEALTHCHECK_SCRIPT"; then
    echo "⚠️  Cron entry already exists"
  else
    (crontab -l 2>/dev/null; echo "$CRON_LINE") | crontab -
    echo "✅ Cron job added"
  fi

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/install.sh (reported line 106)May include surrounding context.

sh
echo ""
  echo "Commands:"
  echo "  crontab -l  # view"
  echo "  crontab -e  # edit"
  echo "  tail -f ${WORKSPACE}/job-execution-monitor.log"
fi

Vague Triggers

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This manifest-like JSON file defines a dynamic job name pattern flight-tracker-* and only notes that wildcards are supported, without specifying what names are in scope or excluded. Such a broad pattern can ambiguously match unintended job names, which fits the vague-trigger category for manifest files.

Content

No source excerpt is available for this finding.