Back to skill

Security audit

Gateway Monitor (macOS)

Security checks for vulnerabilities and agentic risk

Overview

The skill installs a persistent gateway monitor, but it also exposes unauthenticated network APIs, can restart or restore OpenClaw configuration, and reads provider credentials without clear disclosure.

Review before installing. This should be treated as a privileged local service: bind it to 127.0.0.1, add authentication, remove GET-based administrative actions, disable automatic MiniMax credential reading unless explicitly opted in, and avoid installing it on shared or untrusted networks until those issues are addressed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
assets/gateway-monitor/server.js:1642
Finding

Unauthenticated sensitive monitoring APIs are exposed on all network interfaces

Content
View full analysis
{ const parsed = url.parse(req.url, true); const pathname = parsed.pathname; if (pathname === '/api/gateway-status') { const gateway = gatewayStatus(); return json(res, { now: new Date().toISOString(), gateway, model: currentModel(), context: sessionContextStatus(), metrics: buildMetrics(gateway) }); } if (pathname === '/api/sessions') { const ctx = sessionContextStatus(); return json(res, { now: new Date().toISOString(), ok: ctx.ok, primary: ctx.ok ? { percentUsed: ctx.percentUsed, totalTokens: ctx.totalTokens, contextTokens: ctx.contextTokens, remainingTokens: ctx.remainingTokens, model: ctx.model, abortedLastRun: ctx.abortedLastRun } : null, sessions: ctx.sessions || [], error: ctx.error || null }); } if (pathname === '/api/bootstrap') { return json(res, buildBootstrapSnapshot()); } if (pathname === '/api/logs') { const hasLevelParam = Object.prototype.hasOwnProperty.call(parsed.query || {}, 'level'); const levelFilter = hasLevelParam ? String(parsed.query.level || '') : 'error,warn'; const entries = filterEntries(loadEntries(ONE_HOUR_MS), { signature: parsed.query.signature, level: levelFilter, keyword: parsed.query.keyword }); return json(res, { now: new Date().toISOString(), count: entries.length, items: entries.slice(0, 150) }); } if (pathname === '/api/diagnostics') { return json(res, buildDiagnosticsSnapshot()); } }); ``` ```js server.listen(PORT, '0.0.0.0', () => { console.log(`Gateway monitor running: http://127.0.0.1:${PORT}`); ...[truncated 2199 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
assets/gateway-monitor/server.js:1807
Finding

Unauthenticated GET endpoints can restore configuration and restart the gateway

Content
View full analysis
f.startsWith('openclaw.json.bak.')) .map(f => { const p = path.join(configBackupDir, f); return { name: f, path: p, mtime: fs.statSync(p).mtimeMs }; }) .sort((a, b) => b.mtime - a.mtime); if (backupFiles.length === 0) { return json(res, { ok: false, error: 'No backup files found', configBackupDir }, 404); } const latestBackup = backupFiles[0].name; const backupPath = backupFiles[0].path; fs.copyFileSync(backupPath, openclawJsonPath); const content = fs.readFileSync(openclawJsonPath, 'utf8'); JSON.parse(content); return json(res, { ok: true, restored: latestBackup, backupPath, configPath: openclawJsonPath, message: 'Configuration restored successfully. Gateway restart may be required.' }); } catch (err) { return json(res, { ok: false, error: 'Restore failed', message: err.message, stack: process.env.NODE_ENV === 'development' ? err.st ...[truncated 3282 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
assets/gateway-monitor/server.js:309
Finding

Monitor silently reads a provider credential and transmits it to a remote endpoint

Content
View full analysis
{ const keyInfo = resolveMiniMaxApiKey(); const basePayload = { now: new Date().toISOString(), ok: false, statusMsg: 'unavailable', source: keyInfo.source, windowHours: 5, models: [] }; if (!keyInfo.key) { const result = { ...basePayload, statusMsg: 'missing_api_key', error: 'MiniMax Coding Plan API key not found (env MINIMAX_CP_KEY / auth profile).' }; minimaxCache.value = result; minimaxCache.at ...[truncated 2731 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
assets/gateway-monitor/server.js:32
Finding

Hard-coded oMLX API credential can be transmitted to an arbitrary plaintext endpoint

Content
View full analysis
{ const u = new URL(urlString); const isHttps = u.protocol === 'https:'; const client = isHttps ? https : http; const req = client.request({ protocol: u.protocol, hostname: u.hostname, port: u.port || (isHttps ? 443 : 80), path: `${u.pathname}${u.search}`, method: 'GET', headers, timeout: timeoutMs }, (res) => { ``` ### Technical Analysis The code embeds `8888` as a default bearer credential. Hard-coded credentials encourage reuse and prevent safe per-installation secret rotation. The target URL is configurable and the request helper permits unencrypted HTTP without checking whether the destination remains on loopback. The default endpoint is loopback, which limits interception under the default configuration. However, if `OMLX_MODELS_URL` is changed to a remote HTTP endpoint, the bearer credential is transmitted in plaintext over the network. The code also does not enforce a destination allowlist. ### Attack Path 1. An operator, installer, or compromised user-level configuration changes `OMLX_MODELS_URL` to a re ...[truncated 787 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (48)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The documented purpose presents the skill as a local installer/operator for gateway monitor services, but the analyzed behavior indicates substantially broader functionality: a network-accessible HTTP server, outbound network access, sensitive file/log access, and state-mutating control endpoints. This hidden expansion of capability is dangerous because users may grant trust for routine local service management while unknowingly deploying software that exposes remote control surfaces and collects sensitive operational data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The monitor includes functionality far beyond local gateway health checks: it is wired to inspect MiniMax account quota state, query OMLX/OpenAPI services, and check GitHub releases. This broadens the attack surface and causes the service to handle credentials and outbound communications unrelated to the declared monitoring purpose, violating least privilege and creating unnecessary data exposure paths.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code reads a local auth profile to extract an API key, then uses that credential for outbound requests to an external MiniMax endpoint. A local monitoring service should not silently harvest credentials from disk for unrelated remote calls, because compromise or misuse of the monitor exposes account data and can leak or abuse the user's API access.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The service exposes a configuration-restore endpoint that writes to ~/.openclaw/openclaw.json, which is a state-changing capability not implied by a monitor. Any party that can reach this HTTP service can trigger restoration of a prior config, potentially downgrading security settings, re-enabling unsafe plugins, or disrupting service operation.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
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).

Content

Scanner excerpt · scripts/uninstall.sh (reported line 11)May include surrounding context.

sh
for label in ai.openclaw.gateway-monitor ai.openclaw.gateway-watchdog; do
  launchctl bootout "gui/${UID_NUM}/${label}" 2>/dev/null || true
  launchctl disable "gui/${UID_NUM}/${label}" 2>/dev/null || true
  rm -f "$LA_DIR/${label}.plist"
done

rm -rf "$TARGET_DIR"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README instructs users to run install/uninstall scripts that likely modify LaunchAgent configuration and local system state, but it gives no warning that these commands perform persistent system changes. In a security-sensitive context, omission of this warning increases the chance that users execute privileged or persistence-establishing actions without informed consent or prior review of the scripts.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes shell-based installation, status, and uninstall scripts but does not declare any tool scope or permissions, which weakens reviewability and allows code execution capabilities to be introduced without explicit disclosure. In a skill that installs LaunchAgents and manipulates local services, missing tool restrictions increases the chance of unintended or unsafe command execution.

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 · assets/gateway-monitor/gateway-watchdog.sh (reported line 18)May include surrounding context.

sh
if ! status_json="$($NODE_BIN $OPENCLAW_JS gateway status --json 2>>"$ERR_FILE")"; then
  echo "[$(date '+%F %T')] status check failed -> reload plist" >> "$LOG_FILE"
  launchctl unload "$HOME/Library/LaunchAgents/${GATEWAY_LABEL}.plist" 2>/dev/null || true
  launchctl load "$HOME/Library/LaunchAgents/${GATEWAY_LABEL}.plist" >> "$LOG_FILE" 2>>"$ERR_FILE"
  exit 0
fi

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 · assets/gateway-monitor/gateway-watchdog.sh (reported line 32)May include surrounding context.

sh
if ! status_json="$($NODE_BIN $OPENCLAW_JS gateway status --json 2>>"$ERR_FILE")"; then
  echo "[$(date '+%F %T')] status check failed -> reload plist" >> "$LOG_FILE"
  launchctl unload "$HOME/Library/LaunchAgents/${GATEWAY_LABEL}.plist" 2>/dev/null || true
  launchctl load "$HOME/Library/LaunchAgents/${GATEWAY_LABEL}.plist" >> "$LOG_FILE" 2>>"$ERR_FILE"
  exit 0
fi

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest emphasizes installing and operating a local gateway-monitor stack with status/repair workflows, but this public monitoring page also exposes direct destructive actions to restart the Gateway and restore configuration. Those are operational control capabilities, not merely monitoring/validation display behavior, and materially expand what this file enables.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This public-facing monitor page lets a user change the GitHub repository used for update checks, which influences a later backend request to /api/omlx-update-check. Even though the client regex constrains the value to owner/repo format, exposing control over a server-side outbound fetch target can enable misuse, information leakage, or SSRF-adjacent behavior if the backend constructs requests unsafely or trusts this parameter too broadly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The diagnostics action copies a full diagnostics snapshot to the clipboard without previewing contents or warning that it may contain sensitive operational data. Clipboard contents are accessible to the local user environment and may be pasted into chats, tickets, or other destinations accidentally, causing unintended disclosure of service metadata, logs, or configuration details.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · assets/gateway-monitor/server.js (reported line 494)May include surrounding context.

js
async function loadOmlxUpdateInfo(force = false, currentVersion = '', repoInput = OMLX_GITHUB_REPO) {
  const repo = normalizeGithubRepo(repoInput);
  const releaseApi = `https://api.github.com/repos/${repo}/releases/latest`;
  const tagsApi = `https://api.github.com/repos/${repo}/tags?per_page=1`;
  const cacheKey = repo.toLowerCase();
  const cacheBucket = omlxUpdateCacheMap.get(cacheKey) || { at: 0, value: null, inFlight: null };

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · assets/gateway-monitor/server.js (reported line 495)May include surrounding context.

js
async function loadOmlxUpdateInfo(force = false, currentVersion = '', repoInput = OMLX_GITHUB_REPO) {
  const repo = normalizeGithubRepo(repoInput);
  const releaseApi = `https://api.github.com/repos/${repo}/releases/latest`;
  const tagsApi = `https://api.github.com/repos/${repo}/tags?per_page=1`;
  const cacheKey = repo.toLowerCase();
  const cacheBucket = omlxUpdateCacheMap.get(cacheKey) || { at: 0, value: null, inFlight: null };

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · assets/gateway-monitor/server.js (reported line 1720)May include surrounding context.

js
async function loadOmlxUpdateInfo(force = false, currentVersion = '', repoInput = OMLX_GITHUB_REPO) {
  const repo = normalizeGithubRepo(repoInput);
  const releaseApi = `https://api.github.com/repos/${repo}/releases/latest`;
  const tagsApi = `https://api.github.com/repos/${repo}/tags?per_page=1`;
  const cacheKey = repo.toLowerCase();
  const cacheBucket = omlxUpdateCacheMap.get(cacheKey) || { at: 0, value: null, inFlight: null };

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · assets/gateway-monitor/server.js (reported line 1721)May include surrounding context.

js
async function loadOmlxUpdateInfo(force = false, currentVersion = '', repoInput = OMLX_GITHUB_REPO) {
  const repo = normalizeGithubRepo(repoInput);
  const releaseApi = `https://api.github.com/repos/${repo}/releases/latest`;
  const tagsApi = `https://api.github.com/repos/${repo}/tags?per_page=1`;
  const cacheKey = repo.toLowerCase();
  const cacheBucket = omlxUpdateCacheMap.get(cacheKey) || { at: 0, value: null, inFlight: null };

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

A sensitive configuration file is overwritten via a GET request gated only by ?confirm=true. Because the server listens on 0.0.0.0 and has no authentication, this endpoint is vulnerable to trivial cross-site request triggering and unauthorized remote invocation, allowing attackers to force config rollback with a single URL fetch.

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

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

sh
MONITOR_LABEL="ai.openclaw.gateway-monitor"
WATCHDOG_LABEL="ai.openclaw.gateway-watchdog"
MONITOR_PLIST="$LA_DIR/${MONITOR_LABEL}.plist"
WATCHDOG_PLIST="$LA_DIR/${WATCHDOG_LABEL}.plist"

NODE_BIN="$(command -v node || true)"

Session Persistence

Medium
Category
Rogue Agent
Confidence
89% confidence
Finding

This line begins writing a LaunchAgent plist that configures an always-on background service in the user's session. Creating a persistent autorun definition is security-relevant because any compromise or malicious modification of the deployed server.js would be relaunched automatically on login.

Content

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

sh
backup_file "$MONITOR_PLIST"
backup_file "$WATCHDOG_PLIST"

cat > "$MONITOR_PLIST" <<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">

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
assets/gateway-monitor/server.js:27