T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/setup.sh:7
- Finding
- Persistent Arbitrary Network Requests Through an Unvalidated Heartbeat URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:7-53`; `scripts/heartbeat.sh:5-56` **Vulnerability Type**: Unvalidated network destination resulting in persistent blind SSRF behavior **Risk Level**: Medium ### Complete Code Snippet From `scripts/setup.sh`: ```bash PING_URL="${1:?Usage: setup.sh <PING_URL> [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 <?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> <key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>${HEARTBEAT_SCRIPT}</string> <string>${PING_URL}</string> <string>${INTERVAL}</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> </dict> </plist> EOF launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH" ``` From `scripts/heartbeat.sh`: ```bash PING_URL="${1:?Usage: heartbeat.sh <PING_URL> [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
- <![CDATA[ ## Remediation Suggestions 1. Parse the supplied URL before creating the LaunchAgent. 2. Require the `https` scheme. 3. Allowlist the exact expected hostname, such as `hc-ping.com`. 4. Reject embedded credentials, unexpected ports, fragments, and malformed paths. 5. Validate the check identifier against the expected Healthchecks.io UUID format. 6. Resolve and reject loopback, private, link-local, multicast, and otherwise non-public destinations where practical. 7. Construct the complete endpoint internally from a validated UUID rather than accepting an arbitrary full URL. 8. Validate `INTERVAL` as a bounded positive integer to prevent unintended rapid request loops. 9. Fail closed and do not install or bootstrap the LaunchAgent when validation fails. For example, the preferred interface should accept only a UUID: ```bash CHECK_UUID="${1:?Usage: setup.sh <CHECK_UUID> [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}" ``` ]]>
