Back to skill

Security audit

Gateway Watchdog Discord

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed OpenClaw health watchdog, but its Spark check can read a local bearer token and send it to any configured Spark URL without validating the destination.

Review this before installing if you use Spark tokens. Keep SPARK_API_URL on a trusted loopback endpoint unless you intentionally use a remote Spark service, and avoid relying on the automatic ~/.openclaw/.env token fallback for remote endpoints. Only enable cron or LaunchAgent mode if you want recurring background checks and Discord notifications.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/gateway-watchdog.sh:201
Finding

Spark Bearer Token Can Be Disclosed to an Arbitrary Configured Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/gateway-watchdog.sh, lines 14, 58–65, and 201–213
Vulnerability Type: Credential disclosure through an unrestricted destination
Risk Level: High

Vulnerable Code

bash
OPENCLAW_ENV_FILE="${OPENCLAW_ENV_FILE:-$HOME/.openclaw/.env}"
bash
for key in \
  DISCORD_WEBHOOK_URL DISCORD_BOT_TOKEN DISCORD_CHANNEL_ID \
  SPARK_API_URL SPARK_API_TOKEN LOCAL_API_URL DASHBOARD_PORT \
  GW_WATCHDOG_FAIL_THRESHOLD GW_WATCHDOG_COOLDOWN_SECONDS \
  GW_WATCHDOG_HEALTH_TIMEOUT_MS GW_WATCHDOG_LOCK_TIMEOUT_SECONDS; do
  if value=$(read_env_value "$key" "$CONFIG_ENV_FILE" 2>/dev/null); then
    printf -v "$key" '%s' "$value"
    export "$key"
  fi
done
bash
check_spark() {
  local spark_api="${SPARK_API_URL:-http://127.0.0.1:17070}"
  local spark_token="${SPARK_API_TOKEN:-}"
  local response
  local curl_args=(-fsS --max-time 5 -H "Accept: application/json")

  if [[ -z "$spark_token" ]]; then
    spark_token=$(read_env_value "SPARK_API_TOKEN" "$OPENCLAW_ENV_FILE" 2>/dev/null || true)
  fi
  if [[ -n "$spark_token" ]]; then
    curl_args+=(-H "Authorization: Bearer ${spark_token}")
  fi

  if ! response=$(curl "${curl_args[@]}" "${spark_api}/status" 2>/dev/null); then
    echo "fail"
    return
  fi

Technical Analysis

The watchdog automatically reads SPARK_API_TOKEN from the general OpenClaw environment file when the token is not otherwise configured. It then attaches that credential as a bearer token to a request directed by SPARK_API_URL.

Although the default URL is loopback-only, SPARK_API_URL can be supplied through the process environment or the watchdog's config.env. The code does not validate the destination scheme, hostname, resolved address, port, or origin before transmitting the credential. Consequently, an HTTP or HTTPS endpoint controlled by an attacker can receive the token.

Parsing .env ...[truncated 1889 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce loopback destinations by default. Parse SPARK_API_URL and reject hosts other than 127.0.0.1, ::1, or an explicitly approved local hostname.
  2. If remote Spark endpoints are required, introduce a separate explicit opt-in and require HTTPS with certificate verification.
  3. Bind each bearer token to a configured, allowlisted origin. Do not attach a token after redirects to a different origin; explicitly disable redirects or validate every redirect target.
  4. Remove automatic fallback to the general ~/.openclaw/.env file. Prefer an explicit SPARK_API_TOKEN environment variable or a dedicated token file with restrictive permissions.
  5. Validate configuration ownership and permissions before reading secrets, and reject user-writable configuration that is accessible by other accounts.
  6. Consider separate options for authenticated and unauthenticated checks so an endpoint override cannot implicitly trigger credential transmission.
  7. Add tests confirming that bearer tokens are never sent to non-loopback HTTP endpoints or unapproved origins.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (17)

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · scripts/gateway-watchdog.sh (reported line 213)May include surrounding context.

sh
curl_args+=(-H "Authorization: Bearer ${spark_token}")
  fi

  if ! response=$(curl "${curl_args[@]}" "${spark_api}/status" 2>/dev/null); then
    echo "fail"
    return
  fi

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · README.md (reported line 22)May include surrounding context.

md
- Probes configured Spark, Local API Hub, and Dashboard loopback health endpoints on every run
- Sends Discord incident messages (`ALERT`, `RECOVERED`)
- Deduplicates noisy failures using threshold + cooldown
- Does not rewrite config, promote baselines, run `doctor --fix`, or restart services
- Runs via two paths:
  - Internal OpenClaw cron (normal operations)
  - External macOS LaunchAgent fallback (when Gateway is unhealthy)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises shell-based installation and execution steps but does not declare an explicit tool scope such as permissions or allowed-tools. That increases the attack surface because an agent may invoke shell commands for installation, health checks, or persistence setup without a clearly bounded capability declaration, making review and enforcement harder.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

The skill explicitly supports cron execution, LaunchAgent installation, local state files, and ongoing watchdog operation, which are forms of persistence even if framed as operator-driven monitoring. Persistence mechanisms are security-relevant because they create recurring execution paths that can be repurposed, survive beyond a single session, and continuously access local services and notification channels.

Content

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

md
---
name: gateway-watchdog
description: Monitor the OpenClaw Gateway plus configured Spark, Local API Hub, and Dashboard loopback health endpoints with a read-only watchdog state machine, local state files, cooldown dedupe, and optional Discord alerts on macOS. Use for explicitly configured foreground checks, cron execution, LaunchAgent installation, recovery notification, and low-noise incident reporting; it does not rewrite OpenClaw config or restart services.
metadata:
  openclaw:
    version: "1.0.4"

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The referenced LaunchAgent plist template and installer indicate the skill can install a user-level background job. User persistence on macOS is sensitive because it enables repeated unattended execution, which could be abused to continue probing local services or exfiltrating status data if the script or configuration is later modified.

Content

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

md
- `scripts/gateway-watchdog.sh` - health checks + state machine + Discord notification.
- `scripts/install-launchd.sh` - installs a user LaunchAgent from template.
- `references/com.openclaw.gateway-watchdog.plist.template` - launchd template.
- `references/cron-agent-turn.md` - isolated cron prompt template.

## Health checks

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The duration formatter hard-codes Chinese time-unit labels, and the alerting paths also use Chinese message text for recovery and outage notifications. This forces a specific language in operational messages with no indication that users can choose or configure locale, which conflicts with the language/locale policy for all file types.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/gateway-watchdog.sh (reported line 171)May include surrounding context.

sh
payload=$(printf '{"content":"**%s**\\n%s"}' "$title" "$body")

  if [[ -n "$DISCORD_WEBHOOK_URL" ]]; then
    echo "$payload" | curl -sS -X POST "$DISCORD_WEBHOOK_URL" \
      -H "Content-Type: application/json" --data @- >/dev/null 2>&1 || true
    return
  fi

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/gateway-watchdog.sh (reported line 177)May include surrounding context.

sh
fi

  if [[ -n "$DISCORD_BOT_TOKEN" && -n "$DISCORD_CHANNEL_ID" ]]; then
    echo "$payload" | curl -sS -X POST \
      "https://discord.com/api/v10/channels/${DISCORD_CHANNEL_ID}/messages" \
      -H "Authorization: Bot $DISCORD_BOT_TOKEN" \
      -H "Content-Type: application/json" --data @- >/dev/null 2>&1 || true

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Both recovery and incident alerts use fixed Chinese strings such as '已恢复' and '断连告警' in the messages sent to Discord. Because the script provides no user-selectable language setting or documented locale constraint, it imposes a language choice on all recipients.

Content

No source excerpt is available for this finding.

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

md
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
WATCHDOG_SCRIPT="$BASE_DIR/scripts/gateway-watchdog.sh"
TEMPLATE="$BASE_DIR/references/com.openclaw.gateway-watchdog.plist.template"

LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"

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

sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
WATCHDOG_SCRIPT="$BASE_DIR/scripts/gateway-watchdog.sh"
TEMPLATE="$BASE_DIR/references/com.openclaw.gateway-watchdog.plist.template"

LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"

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

sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
WATCHDOG_SCRIPT="$BASE_DIR/scripts/gateway-watchdog.sh"
TEMPLATE="$BASE_DIR/references/com.openclaw.gateway-watchdog.plist.template"

LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"

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

sh
LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"
LAUNCHD_PLIST="$LAUNCHD_DIR/${LAUNCHD_LABEL}.plist"
INTERVAL="${GW_WATCHDOG_INTERVAL_SECONDS:-30}"
LOAD_AFTER_INSTALL=0

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

sh
LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"
LAUNCHD_PLIST="$LAUNCHD_DIR/${LAUNCHD_LABEL}.plist"
INTERVAL="${GW_WATCHDOG_INTERVAL_SECONDS:-30}"
LOAD_AFTER_INSTALL=0

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

sh
LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"
LAUNCHD_PLIST="$LAUNCHD_DIR/${LAUNCHD_LABEL}.plist"
INTERVAL="${GW_WATCHDOG_INTERVAL_SECONDS:-30}"
LOAD_AFTER_INSTALL=0

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

sh
LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"
LAUNCHD_PLIST="$LAUNCHD_DIR/${LAUNCHD_LABEL}.plist"
INTERVAL="${GW_WATCHDOG_INTERVAL_SECONDS:-30}"
LOAD_AFTER_INSTALL=0

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

sh
LAUNCHD_LABEL="${GW_WATCHDOG_LABEL:-com.openclaw.gateway-watchdog}"
LAUNCHD_DIR="$HOME/Library/LaunchAgents"
LAUNCHD_PLIST="$LAUNCHD_DIR/${LAUNCHD_LABEL}.plist"
INTERVAL="${GW_WATCHDOG_INTERVAL_SECONDS:-30}"
LOAD_AFTER_INSTALL=0

Static analysis

No suspicious patterns detected.