Back to skill

Security audit

Skill

Security checks for vulnerabilities and agentic risk

Overview

This watchdog mostly does what it claims, but installation creates persistent cron jobs and stores sensitive tokens while using unsafe crontab handling that can affect unrelated scheduled tasks.

Review and back up your crontab before installing. Only install if you are comfortable storing Telegram and OpenClaw gateway tokens in a local env file and running two background cron jobs every 15 minutes. Prefer a version that uses a safe temporary file or no temp file, exact managed cron markers, shell-safe path quoting, and token-rotation guidance on uninstall.

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

Warning
Location
scripts/setup.sh:195
Finding
Predictable Temporary File Allows Symlink-Based File Clobbering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh`, lines 195-202 **Vulnerability Type**: Unsafe predictable temporary file **Risk Level**: Medium ### Vulnerable Code ```bash crontab -l 2>/dev/null | grep -v 'claude-watchdog' | grep -v 'status-check\.py' | grep -v 'latency-probe\.py' > /tmp/crontab-clean || true { cat /tmp/crontab-clean echo "*/15 * * * * $PYTHON3 $STATUS_SCRIPT >> /dev/null 2>&1 # claude-watchdog" echo "*/15 * * * * $PYTHON3 $LATENCY_SCRIPT >> /dev/null 2>&1 # claude-watchdog" } | crontab - rm -f /tmp/crontab-clean ``` ### Technical Analysis The setup script uses the fixed, globally predictable path `/tmp/crontab-clean`. Shell redirection opens this path with truncation before writing the filtered crontab. The script does not securely create the file, verify its ownership or type, or prevent symbolic-link traversal. On systems without effective temporary-directory symlink protections, another local process can create `/tmp/crontab-clean` as a symbolic link to a file writable by the user who later runs the setup script. The redirection can then truncate and overwrite the linked file. The linked file's resulting contents are subsequently passed to `crontab`, potentially adding unintended content to the user's scheduled tasks. Some Linux configurations mitigate this through protected symlink and protected regular-file settings, but the implementation remains unsafe and non-portable. ### Attack Path 1. A local attacker predicts that the victim will run `scripts/setup.sh`. 2. The attacker creates `/tmp/crontab-clean` as a symbolic link to a file that the victim can modify. 3. The victim runs the setup script. 4. Shell redirection follows the symbolic link and truncates the target file. 5. Filtered crontab data is written to the target. 6. The script reads the same path and submits its contents to `crontab`. 7. Depending on the chosen target and its existing content, this causes file corruption and may intr ...[truncated 484 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid filesystem-backed temporary storage where possible. Build the replacement crontab through a pipeline or shell variable and submit it directly. If a temporary file is necessary: 1. Create it with `mktemp`. 2. Install an `EXIT` trap to remove it. 3. Ensure it is owned by the current user and has mode `0600`. 4. Do not reuse a predictable filename. Example: ```bash tmp_crontab="$(mktemp "${TMPDIR:-/tmp}/claude-watchdog.XXXXXX")" trap 'rm -f "$tmp_crontab"' EXIT chmod 600 "$tmp_crontab" crontab -l 2>/dev/null | grep -v 'claude-watchdog' > "$tmp_crontab" || true { cat "$tmp_crontab" # Add securely generated Skill-owned entries here. } | crontab - ``` The preferred design is to manage an exactly delimited cron block and avoid broad filtering, as described in the next finding. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:24
Finding
Substring-Based Cron Filtering Can Delete Unrelated Scheduled Tasks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh`, lines 24-25 and line 195 **Vulnerability Type**: Overbroad modification of persistent scheduled tasks **Risk Level**: Medium ### Vulnerable Code Uninstallation removes every cron line containing the Skill name: ```bash if crontab -l 2>/dev/null | grep -q 'claude-watchdog'; then crontab -l 2>/dev/null | grep -v 'claude-watchdog' | crontab - ``` Setup additionally removes lines containing generic script filenames: ```bash crontab -l 2>/dev/null | grep -v 'claude-watchdog' | grep -v 'status-check\.py' | grep -v 'latency-probe\.py' > /tmp/crontab-clean || true ``` ### Technical Analysis The script edits the user's complete crontab using unanchored substring filtering. It assumes that every line containing `claude-watchdog`, `status-check.py`, or `latency-probe.py` belongs to this Skill. The latter two names are generic and may legitimately be used by unrelated applications. Even `claude-watchdog` could appear in an unrelated command, path, comment, environment value, or notification recipient. Any matching line is silently excluded before the reconstructed crontab is installed. This exceeds the minimum modification necessary for persistence. Continuous monitoring reasonably requires scheduled execution, but setup and uninstallation should modify only entries that are unambiguously owned by this Skill. ### Attack Path 1. The user already has an unrelated cron entry whose command or comment contains one of the filtered substrings. 2. The user runs setup or `setup.sh --uninstall`. 3. `grep -v` removes the unrelated line from the reconstructed crontab. 4. The script installs the filtered output with `crontab -`. 5. The unrelated scheduled task stops running without a warning or confirmation. A malicious local actor who can cause an important cron line to include one of these strings could also arrange for that task to be removed the next time the victim reconfigures or uninstalls ...[truncated 512 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Manage only exact Skill-owned entries. Recommended approaches include: 1. Surround generated entries with unique begin and end markers. 2. Remove or replace only the content inside that exact marked block. 3. Alternatively, compare against fully anchored, canonical cron lines rather than generic filenames. 4. Back up the existing crontab before replacement. 5. Display the proposed changes or require confirmation before deleting ambiguous entries. Example ownership markers: ```cron # BEGIN claude-watchdog-managed */15 * * * * /absolute/path/python3 /absolute/path/status-check.py */15 * * * * /absolute/path/python3 /absolute/path/latency-probe.py # END claude-watchdog-managed ``` Setup and uninstallation should remove only the lines between these exact markers. They should not remove every line containing `status-check.py` or `latency-probe.py`. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/setup.sh:198
Finding
Unquoted Paths in Cron Entries Permit Shell Interpretation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh`, lines 198-199 **Vulnerability Type**: Cron command injection through unquoted paths **Risk Level**: Low ### Vulnerable Code ```bash echo "*/15 * * * * $PYTHON3 $STATUS_SCRIPT >> /dev/null 2>&1 # claude-watchdog" echo "*/15 * * * * $PYTHON3 $LATENCY_SCRIPT >> /dev/null 2>&1 # claude-watchdog" ``` ### Technical Analysis `PYTHON3`, `STATUS_SCRIPT`, and `LATENCY_SCRIPT` are inserted directly into cron command lines without shell-safe quoting. Cron executes command fields through a shell, so whitespace and shell metacharacters in an installation path are interpreted as syntax rather than as part of a filename. `PYTHON3` normally comes from `command -v python3` and is comparatively constrained. However, `STATUS_SCRIPT` and `LATENCY_SCRIPT` derive from the directory containing the setup script. If the project is placed in a directory containing spaces, the cron jobs can fail. If an attacker can influence that directory name and include shell metacharacters, the generated persistent command may execute attacker-selected shell operations. The setup script itself must be run from the attacker-influenced location, so exploitation requires local control over or influence on the installation path. This limits likelihood, but cron execution every 15 minutes makes a successful injection persistent. ### Attack Path 1. An attacker causes the project to be placed in, or invoked from, a path containing shell metacharacters. 2. The victim runs `scripts/setup.sh` from that project location. 3. Setup interpolates the unquoted path into the generated cron entries. 4. Cron installs the entries without validating the command path. 5. At the next 15-minute interval, the cron shell interprets the embedded metacharacters. 6. Attacker-selected commands execute as the user who installed the cron entries. 7. Execution repeats every 15 minutes until the entries are removed. With a benign path containing only s ...[truncated 672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate cron commands using a shell-safe quoting function. 2. Reject newline, carriage-return, NUL, and other control characters in generated paths. 3. Prefer installing a fixed wrapper script at a controlled path containing no shell metacharacters. 4. Verify that all referenced files are regular files owned by the expected user before installing the entries. 5. Print and confirm the exact generated cron commands during interactive setup. When quoting for cron, account for the shell used by cron rather than assuming Bash-specific escaping. A robust approach is to single-quote each path and replace every embedded single quote with the standard shell-safe sequence. The generated result should conceptually resemble: ```cron */15 * * * * '/usr/bin/python3' '/home/user/path with spaces/scripts/status-check.py' >> /dev/null 2>&1 # claude-watchdog ``` A fixed, controlled installation directory is preferable to persisting commands that reference an arbitrary source directory. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The top-level description understates important behaviors: interactive credential collection, extraction and storage of sensitive local tokens, cron-based persistence, and uninstall/data-deletion behavior. Even if these functions are legitimate for a monitoring tool, hiding or omitting them from the declared behavior weakens informed consent and makes it easier for users to authorize sensitive operations they did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The top-level description understates important behaviors: interactive credential collection, extraction and storage of sensitive local tokens, cron-based persistence, and uninstall/data-deletion behavior. Even if these functions are legitimate for a monitoring tool, hiding or omitting them from the declared behavior weakens informed consent and makes it easier for users to authorize sensitive operations they did not expect.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
HTTP $HTTP_CODE)." >&2
    echo "  Check your bot token, chat ID, and topic ID." >&2
    echo "  Config was saved — you can fix values in $ENV_FILE and retry." >&2
    echo "  Continuing setup anyway (cron jobs will retry on next run)." >&2
fi

# Install cron jobs
STATUS_SCRIPT="$SKILL_DIR/scripts/status-check.py"
LATENCY_SCRIPT="$SKILL_DIR/scripts/latency-probe.py"

# Remove old entries if any
crontab -l 2>/dev/null | grep -v 'claude-watchdog' | grep -v 'status-check\.py' | grep -v 'latency-probe\.py' > /tmp/crontab-clean || true
{
    cat /tmp/crontab-clean
    echo "*/15 * * * * $PYTHON3 $STATUS_SCRIPT >> /dev/null 2>&1 # claude-watchdog"
    echo "*/15 * * * * $PYTHON3 $LATENCY_SCRIPT >> /dev/null 2>&1 # claude-watchdog"
} | crontab -
rm -f /tmp/crontab-clean

echo "✓ Cron jobs installed (every 15 minutes)."

# Run initial status check
echo ""
echo "Running initial status check..."
if "$PYTHON3" "$STATUS_SCRIPT"; then
    echo "✓ Status check passed."
else
    echo "✗ Statu
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo "*/15 * * * * $PYTHON3 $STATUS_SCRIPT >> /dev/null 2>&1 # claude-watchdog"
    echo "*/15 * * * * $PYTHON3 $LATENCY_SCRIPT >> /dev/null 2>&1 # claude-watchdog"
} | crontab -
rm -f /tmp/crontab-clean

echo "✓ Cron jobs installed (every 15 minutes)."
Confidence
95% 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).

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises significant capabilities in its metadata and documentation—reading environment variables, storing secrets, using shell commands, writing files, scheduling cron jobs, and making network requests—but does not declare an explicit tool scope or permissions boundary. This increases risk because users and enforcement layers cannot easily distinguish expected behavior from overbroad access, making accidental or unauthorized actions harder to review or constrain.

Session Persistence

Medium
Category
Rogue Agent
Content
### Security Note

The env file contains sensitive tokens (Telegram bot token, gateway token). The setup script sets permissions to `600` (owner-only read/write). If you create or edit the file manually, ensure restricted permissions:

```bash
chmod 600 ~/.openclaw/skills/claude-watchdog/claude-watchdog.env
Confidence
86% confidence
Finding
The skill intentionally stores long-lived secrets and operational state on disk and installs cron jobs for recurring execution. While this is normal for a watchdog utility, it does create persistence: compromise of the user account or skill files could expose tokens or allow the scheduled task to be modified for ongoing abuse.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
The env file contains sensitive tokens (Telegram bot token, gateway token). The setup script sets permissions to `600` (owner-only read/write). If you create or edit the file manually, ensure restricted permissions:

```bash
chmod 600 ~/.openclaw/skills/claude-watchdog/claude-watchdog.env
```

## Alert Examples
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
echo ""

    # Remove cron jobs
    if crontab -l 2>/dev/null | grep -q 'claude-watchdog'; then
        crontab -l 2>/dev/null | grep -v 'claude-watchdog' | crontab -
        echo "✓ Cron jobs removed."
    else
Confidence
95% confidence
Finding
The uninstall path enumerates and edits the user's crontab, confirming that the skill installs persistent scheduled execution. For a monitoring skill this persistence is aligned with functionality, but it still grants ongoing execution on the host and can continue operating after initial setup, increasing risk if the scripts are later modified or compromised.

Session Persistence

Medium
Category
Rogue Agent
Content
# Remove cron jobs
    if crontab -l 2>/dev/null | grep -q 'claude-watchdog'; then
        crontab -l 2>/dev/null | grep -v 'claude-watchdog' | crontab -
        echo "✓ Cron jobs removed."
    else
        echo "No cron jobs found."
Confidence
95% confidence
Finding
This line removes prior cron entries matching the skill name, which is part of managing persistent scheduled tasks. The behavior is not covert, but it still manipulates recurring execution state on the system and thus qualifies as persistence-related behavior with moderate risk if users do not fully understand it.

External Transmission

Medium
Category
Data Exfiltration
Content
echo ""
echo "Telegram Chat ID — to find yours:"
echo "  1. Send any message to your bot"
echo "  2. Visit https://api.telegram.org/bot<TOKEN>/getUpdates"
echo "  3. Look for \"chat\":{\"id\":YOUR_ID}"
echo "  (Or message @userinfobot on Telegram for your personal chat ID)"
read -rp "> " chat_id
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
echo ""
echo "Telegram Chat ID — to find yours:"
echo "  1. Send any message to your bot"
echo "  2. Visit https://api.telegram.org/bot<TOKEN>/getUpdates"
echo "  3. Look for \"chat\":{\"id\":YOUR_ID}"
echo "  (Or message @userinfobot on Telegram for your personal chat ID)"
read -rp "> " chat_id
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
echo ""
echo "Telegram Chat ID — to find yours:"
echo "  1. Send any message to your bot"
echo "  2. Visit https://api.telegram.org/bot<TOKEN>/getUpdates"
echo "  3. Look for \"chat\":{\"id\":YOUR_ID}"
echo "  (Or message @userinfobot on Telegram for your personal chat ID)"
read -rp "> " chat_id
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
echo ""
echo "Telegram Chat ID — to find yours:"
echo "  1. Send any message to your bot"
echo "  2. Visit https://api.telegram.org/bot<TOKEN>/getUpdates"
echo "  3. Look for \"chat\":{\"id\":YOUR_ID}"
echo "  (Or message @userinfobot on Telegram for your personal chat ID)"
read -rp "> " chat_id
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The setup script collects and persists an OpenClaw gateway authentication token in a local env file so the watchdog can perform authenticated latency probes. That behavior exceeds pure passive outage monitoring and creates a new secret-handling surface: compromise of the local account, misconfigured file access, backups, or later script bugs could expose the gateway token.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The example command prints the gateway auth token directly to the terminal, which can expose the credential through shell scrollback, screen recording, terminal logging, shared sessions, or shoulder-surfing. Even though this is only guidance text, it instructs users to handle a secret in an unnecessarily exposed way.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
PROBE_MODEL=$probe_model
PROBE_AGENT_ID=$probe_agent_id
EOF
chmod 600 "$ENV_FILE"
echo ""
echo "✓ Config written to $ENV_FILE (permissions: 600)"
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
fi
TEST_PAYLOAD="$TEST_PAYLOAD}"

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "https://api.telegram.org/bot${bot_token}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "$TEST_PAYLOAD" 2>/dev/null || echo "000")
Confidence
96% confidence
Finding
The script transmits user-supplied Telegram credentials and message metadata to Telegram's API as part of a test alert. This is expected for the skill's functionality, but it is still a real external data transmission involving secrets and third-party infrastructure, so users must understand that bot token, chat ID, and message content leave the local system.

External Transmission

Medium
Category
Data Exfiltration
Content
TEST_PAYLOAD="$TEST_PAYLOAD}"

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "https://api.telegram.org/bot${bot_token}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "$TEST_PAYLOAD" 2>/dev/null || echo "000")
Confidence
96% confidence
Finding
This is the actual outbound POST to Telegram used to verify setup, and it includes a bot token in the request URL plus user chat identifiers in the payload. In context this is expected behavior, but it still represents third-party transmission and sensitive credential use that should be disclosed and handled carefully.

Session Persistence

Medium
Category
Rogue Agent
Content
LATENCY_SCRIPT="$SKILL_DIR/scripts/latency-probe.py"

# Remove old entries if any
crontab -l 2>/dev/null | grep -v 'claude-watchdog' | grep -v 'status-check\.py' | grep -v 'latency-probe\.py' > /tmp/crontab-clean || true
{
    cat /tmp/crontab-clean
    echo "*/15 * * * * $PYTHON3 $STATUS_SCRIPT >> /dev/null 2>&1 # claude-watchdog"
Confidence
97% confidence
Finding
The setup script installs cron-based recurring execution every 15 minutes for two Python scripts, establishing persistent background activity. That is expected for a watchdog, but in security terms it is still meaningful persistence because it creates ongoing code execution and a future exploitation surface if those scripts or their dependencies are tampered with.

Intent-Code Divergence

Low
Confidence
77% confidence
Finding
The module documentation narrowly frames the tool as only making outbound HTTP requests to status.claude.com and the Telegram Bot API. In practice, the script also reads secrets from a local env file and from process environment variables, which is additional capability not reflected in that reviewer-facing note. This is a mild documentation contradiction rather than a core behavior mismatch.

Static analysis

No suspicious patterns detected.