Back to skill

Security audit

Device Heartbeat Monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real heartbeat monitor, but it installs a persistent background job that can repeatedly contact any supplied URL and handles monitoring secrets in exposed ways.

Install only if you intentionally want a macOS LaunchAgent that keeps running after login and periodically sends heartbeat requests. Use only a valid Healthchecks.io ping URL, avoid putting API keys in shell history, and review or rotate the ping URL/API key if they appear in logs, chat, or command transcripts.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:7
Finding

Persistent Arbitrary Network Requests Through an Unvalidated Heartbeat URL

Content
View full analysis
[INTERVAL_SECONDS]}" INTERVAL="${2:-180}" LABEL="ai.openclaw.device-heartbeat" PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" HEARTBEAT_SCRIPT="${SCRIPT_DIR}/heartbeat.sh" cat > "$PLIST_PATH" << EOF Label ${LABEL} ProgramArguments /bin/bash ${HEARTBEAT_SCRIPT} ${PING_URL} ${INTERVAL} RunAtLoad KeepAlive EOF launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH" ``` From `scripts/heartbeat.sh`: ```bash PING_URL="${1:?Usage: heartbeat.sh [INTERVAL_SECONDS]}" INTERVAL="${2:-180}" while true; do HTTP_CODE=$(curl -fsS --retry 2 --max-time 10 -o /dev/null -w "%{http_code}" "$PING_URL" 2>/dev/null) if [ "$HTTP_CODE" = "200" ]; then FAIL_COUNT=0 update_state "up" else FAIL_COUNT=$((FAIL_COUNT + 1)) update_state "down" fi sleep "$INTERVAL" done ``` ### Technical Analysis The Skill's declared purpose requires periodic requests to a Healthchecks.io ping endpoint, so installing a user-level LaunchAgent and sending outbound heartbeats are functionally justified. However, `PING_URL` is accepted without validating its scheme, hostname, port, path, or UUID format. The unvalidated value is written into a persistent LaunchAgent and repeatedly passed t ...[truncated 2063 chars]
Remediation
View remediation
[INTERVAL_SECONDS]}" if ! [[ "$CHECK_UUID" =~ ^[0-9a-fA-F-]{32,36}$ ]]; then echo "Invalid Healthchecks.io check UUID" >&2 exit 1 fi PING_URL="https://hc-ping.com/${CHECK_UUID}" ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:21
Finding

Capability URL and API Credentials Are Exposed Through Plaintext Configuration and Process Arguments

Content
View full analysis
"$PLIST_PATH" << EOF Label ${LABEL} ProgramArguments /bin/bash ${HEARTBEAT_SCRIPT} ${PING_URL} ${INTERVAL} EOF launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH" echo "✅ Heartbeat service installed and started" echo " Plist: $PLIST_PATH" echo " Log: $LOG_DIR/heartbeat.log" echo " URL: ${PING_URL:0:40}..." echo " Interval: ${INTERVAL}s" ``` From `scripts/check.sh`: ```bash API_KEY="${1:?Usage: check.sh [CHECK_UUID]}" CHECK_UUID="$2" if [ -n "$CHECK_UUID" ]; then curl -fsS --max-time 10 \ -H "X-Api-Key: $API_KEY" \ "https://healthchecks.io/api/v3/checks/${CHECK_UUID}" 2>/dev/null else curl -fsS --max-time 10 \ -H "X-Api-Key: $API_KEY" \ "https://healthchecks.io/api/v3/checks/" 2>/dev/null fi ``` ### Technical Analysis The project documentation correctly identifies the full Healthchecks.io ping URL as a secret-like capability: possession of the URL permits forged heartbeat events. Despite that warning, `setup.sh` stores the complete URL as a plaintext `ProgramArguments` value in the LaunchAgent plist. The URL is also partially printed after installation. Depending on URL length and format, the first 40 characters may disclose all or a substantial portion of the capability-bearing identifier. Output may be retained in terminal transcripts, automation logs, support records, or chat mes ...[truncated 2396 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (30)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The same skill description also omits that it can uninstall a local service, stop a LaunchAgent, and delete files from the user's LaunchAgents directory. Concealing destructive or administrative actions inside a monitoring-themed skill raises the risk of unintended service disruption or data loss if triggered by a casual request.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The same skill description also omits that it can uninstall a local service, stop a LaunchAgent, and delete files from the user's LaunchAgents directory. Concealing destructive or administrative actions inside a monitoring-themed skill raises the risk of unintended service disruption or data loss if triggered by a casual request.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises shell-based setup, status, check, and uninstall commands but does not declare any tool restrictions or allowed-tools scope. In an agent environment, missing explicit tool scoping increases the chance the skill can invoke shell actions more broadly than intended, including modifying local services and files.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation phrases are broad enough to match common requests like 'is my Mac online' or generic connectivity questions, which could cause the skill to trigger in contexts where the user did not intend local monitoring setup or local shell inspection. Overbroad routing increases the likelihood of inappropriate execution of shell-backed actions.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
78% confidence
Finding

The skill sets up persistent background monitoring via a LaunchAgent and stores ongoing state/log data under ~/.openclaw/logs. Persistence itself is part of the feature, but in an agent setting it introduces ongoing execution and retained state that may outlive the user's immediate request and could be abused or misunderstood.

Content

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

md
## Setup

### 1. Create a Check on healthchecks.io

Register at https://healthchecks.io, create a Check. Set Period = 3 min, Grace = 5 min. See `references/healthchecks-setup.md` for details.

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

Beginning the heredoc that writes the LaunchAgent plist is part of the mechanism that creates login persistence for the user. In this skill, persistent monitoring is functionally related to device heartbeat checks, but it is still sensitive because it installs autorun behavior that may outlive user awareness or consent.

Content

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

sh
# 停止已有的服务
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true

cat > "$PLIST_PATH" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script silently writes a LaunchAgent plist into ~/Library/LaunchAgents and bootstraps it immediately, creating persistence at user login without any interactive warning or confirmation. In a skill context, this is security-relevant because it changes startup behavior and could surprise users or be abused to maintain ongoing execution beyond the original task.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This duplicate detection corresponds to the same LaunchAgent plist creation and reflects real session persistence. The danger is not from the plist format itself, but from silently establishing recurring execution in the user's account.

Content

Scanner excerpt · scripts/setup.sh (reported line 23)May include surrounding context.

sh
cat > "$PLIST_PATH" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This duplicate detection corresponds to the same LaunchAgent plist creation and reflects real session persistence. The danger is not from the plist format itself, but from silently establishing recurring execution in the user's account.

Content

Scanner excerpt · scripts/setup.sh (reported line 23)May include surrounding context.

sh
cat > "$PLIST_PATH" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
89% confidence
Finding

The XML plist body at this location is part of the persisted LaunchAgent definition that will cause the heartbeat script to run automatically. Given the skill's stated purpose, the behavior is contextually expected, but still security-relevant because it modifies session startup behavior.

Content

Scanner excerpt · scripts/setup.sh (reported line 24)May include surrounding context.

sh
cat > "$PLIST_PATH" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${LABEL}</string>

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The generated LaunchAgent stores a user-supplied URL and runs a background heartbeat script against it on an ongoing basis, but the setup flow does not explicitly warn that repeated network requests will continue after installation. In a remote-device monitoring skill, this behavior is expected, but persistence plus silent outbound traffic still creates privacy and transparency risks if users do not realize monitoring remains active.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

The launchctl bootstrap command activates the written LaunchAgent, making persistence effective immediately for the current user session. This is the key enforcement step for autorun behavior and is dangerous if performed without informed consent, especially in agent skills that may be invoked casually.

Content

Scanner excerpt · scripts/setup.sh (reported line 50)May include surrounding context.

sh
<key>Nice</key>
    <integer>10</integer>
</dict>
</plist>
EOF

launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
#!/bin/bash
# 卸载心跳服务
LABEL="ai.openclaw.device-heartbeat"
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"

Session Persistence

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

sh
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"
echo "Done. Heartbeat service uninstalled."

Session Persistence

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

sh
PLIST_PATH="$HOME/Library/LaunchAgents/${LABEL}.plist"

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null && echo "✅ Service stopped" || echo "⚠️ Service was not running"
rm -f "$PLIST_PATH" && echo "✅ Plist removed: $PLIST_PATH" || echo "⚠️ Plist not found"
echo "Done. Heartbeat service uninstalled."

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script performs a file deletion with rm -f on the user's LaunchAgents plist as part of uninstalling the heartbeat service. Although it logs the action, there is no confirmation prompt or prior warning before executing this destructive step, and shell deletion is safety-relevant for code files.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file’s natural-language instructions are entirely Chinese, which can amount to a language/locale policy issue when no user opt-in or alternative language option is provided. The stated policy specifically calls for flagging skills that force a specific language without user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script's natural-language guidance and comments are presented only in Chinese, which can impose a language requirement on users without opt-in. Under the language/locale policy, this is a natural-language policy concern unless the language constraint is explicitly justified or alternatives are offered.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

This code performs network requests to healthchecks.io and includes the provided API key in the X-Api-Key header. While comments note that an API key is required, there is no runtime disclosure, confirmation, or explicit user-facing warning that the credential will be sent over the network.

Content

No source excerpt is available for this finding.