Back to skill

Security audit

agent-wake

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it advertises, but it creates a high-trust external control path into an agent and handles a gateway token with weak endpoint safeguards.

Install only if you trust every process that can call the script and can keep the gateway token and .env file protected. Keep GATEWAY_URL pinned to a local trusted gateway, avoid connecting this directly to untrusted webhooks or user-controlled text, and review the agent's available tools before allowing automatic wake events.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/agent-wake.py:62
Finding
Arbitrary Caller-Controlled System Message Injection## Vulnerability Details **File Location**: `scripts/agent-wake.py:62-84` **Vulnerability Type**: System-level instruction injection **Risk Level**: Critical **Vulnerable Code**: ```python def wake(message: str, channel_id: str = "") -> bool: """ Fire a cron wake event into the agent session. If channel_id is provided, targets that specific Discord channel session. Otherwise targets the main session. """ if not GATEWAY_TOKEN: print("ERROR: GATEWAY_TOKEN not set. Check your .env file.", file=sys.stderr) return False event_text = message if channel_id: event_text = ( f"{message} -- Send your response to Discord channel {channel_id} " f"(use message tool, action=send, target={channel_id}). " f"Do not respond anywhere else." ) body: dict = { "tool": "cron", "args": { "action": "wake", "text": event_text, "mode": "now", }, } ``` The privileged nature of the injected content is explicitly documented in `SKILL.md:52-58`: ```markdown ## What the agent receives The event text is injected as a system message. Be specific -- the agent acts on what you write: ``` Build finished -- 3 errors fixed, tests passing. Send your response to Discord channel 1475232925724315740... ``` ``` ### Technical Analysis The first command-line argument is accepted as arbitrary text and copied directly into the `text` property of an immediate `cron` wake event. According to the Skill documentation, this text is injected into the target agent session as a system message rather than as untrusted notification data. No validation, instruction filtering, event-type allowlist, trust boundary, or separation between data and instructions is applied. Consequently, a caller that can invoke the script can submit imperative content designed to alter ...[truncated 2327 chars]
Remediation
## Remediation Suggestions 1. Never place caller-controlled text into a system message or another privileged instruction channel. 2. Deliver external completion notifications as explicitly untrusted event or user data, accompanied by an immutable instruction that the content must not be treated as commands. 3. Replace free-form messages with a strict schema containing allowlisted fields such as task identifier, status, timestamp, and a length-limited summary. 4. Reject imperative control fields and validate message length, encoding, and permitted event types. 5. Authenticate each event producer independently and bind its credential to approved agents, sessions, event types, and Discord channels. 6. Do not derive session routing or message-tool destinations directly from caller-controlled channel IDs. Use a server-side allowlist mapping trusted task identities to fixed destinations. 7. Require explicit user confirmation before the agent performs consequential tool calls in response to an external wake event. 8. Apply least privilege to the gateway token and target agent so that a notification producer cannot invoke unrelated gateway tools. 9. Record and monitor event producer identity, destination session, event type, and resulting tool activity.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/agent-wake.py:48
Finding
Gateway Bearer Token Disclosure Through an Unrestricted Configurable Endpoint## Vulnerability Details **File Location**: `scripts/agent-wake.py:48-54, 88-96` **Vulnerability Type**: Credential exfiltration through unsafe endpoint configuration **Risk Level**: High **Vulnerable Code**: ```python # Load .env from script directory only (no workspace .env scanning) load_env(Path(__file__).parent / ".env") GATEWAY_URL = os.environ.get("GATEWAY_URL", "http://localhost:18789").rstrip("/") # Priority: env var > local .env > gateway.cmd GATEWAY_TOKEN = os.environ.get("GATEWAY_TOKEN", "") or read_gateway_token_from_cmd() ``` ```python payload = json.dumps(body).encode("utf-8") req = urllib.request.Request( f"{GATEWAY_URL}/tools/invoke", data=payload, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {GATEWAY_TOKEN}", }, method="POST", ) ``` The fallback credential source is implemented at `scripts/agent-wake.py:36-44`: ```python def read_gateway_token_from_cmd() -> str: """Read OPENCLAW_GATEWAY_TOKEN from gateway.cmd (OpenClaw's token file).""" cmd_path = Path.home() / ".openclaw" / "gateway.cmd" if not cmd_path.exists(): return "" for line in cmd_path.read_text().splitlines(): if "OPENCLAW_GATEWAY_TOKEN=" in line: return line.split("=", 1)[1].strip().strip('"') return "" ``` ### Technical Analysis `GATEWAY_URL` can be supplied through the process environment or a `.env` file beside the script. The value is not validated for scheme, hostname, port, origin, or loopback status. The script then attaches the gateway bearer token to a request sent to that URL. If a gateway token is not explicitly provided, the implementation can recover a higher-value token from `~/.openclaw/gateway.cmd`. As a result, control over endpoint configuration can be combined with automatic credential discovery: an attacker can select a server they control while the script independently l ...[truncated 2034 chars]
Remediation
## Remediation Suggestions 1. Permit only loopback gateway destinations by default, such as `127.0.0.1`, `::1`, or a strictly validated local socket. 2. Reject user-info components, unexpected ports, fragments, non-HTTP schemes, and non-allowlisted hostnames after canonical URL parsing. 3. If remote gateways are required, maintain an explicit endpoint allowlist and require HTTPS with valid certificate verification. 4. Never transmit a gateway credential over plaintext HTTP to a non-loopback address. 5. Disable redirects for authenticated requests or permit redirects only when the destination has the exact same trusted origin. 6. Do not automatically read a privileged token from `gateway.cmd` when the endpoint is externally configurable. 7. Bind credentials to a fixed gateway origin and issue a dedicated, narrowly scoped token for this notification function. 8. Protect the script-adjacent `.env` with restrictive ownership and permissions, and reject files writable by untrusted users. 9. Separate endpoint configuration from secret-bearing execution environments and prevent untrusted CI jobs or subprocesses from overriding `GATEWAY_URL`. 10. Rotate the gateway token immediately if it may have been used with an untrusted endpoint, and review gateway logs for unauthorized invocations.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tainted flow: 'req' from os.environ.get (line 96, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
)

    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            if resp.status == 200:
                print(f"OK: Agent woken (channel={channel_id or 'main'})")
                return True
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: Wake an OpenClaw agent session from an external script or process. Use when a background task (Claude Code CLI, cron job, webhook, price alert, or any script) finishes and you want the agent to automatically receive a notification and respond in the correct Discord channel without manual prompting. Solves the problem of agents not knowing when async work completes.
credentials:
  - name: GATEWAY_TOKEN
    description: OpenClaw gateway auth token. Read from GATEWAY_TOKEN env var, a local .env file next to the script, or auto-detected from ~/.openclaw/gateway.cmd.
    required: true
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
97% confidence
Finding
The documentation states that wake event text is injected as a system message but does not warn that this gives external content elevated influence over agent behavior. Users may pass arbitrary webhook, script, or job output through this channel, unintentionally turning untrusted data into privileged instructions.

Ssd 1

High
Confidence
99% confidence
Finding
This skill's core design lets arbitrary external text steer the agent by delivering it as a system message, which is one of the highest-trust instruction channels. If an external process, webhook payload, or compromised script can influence that text, it can manipulate the agent into taking unintended actions or leaking information into Discord.

Ssd 1

High
Confidence
98% confidence
Finding
The example explicitly encourages instruction-bearing wake messages such as 'Send your response to Discord channel ...', normalizing the use of the wake mechanism as a control plane for downstream agent actions. That makes prompt injection easier because operators are taught to embed operational commands inside externally generated event text.

Credential Access

High
Category
Privilege Escalation
Content
# Load .env from script directory only (no workspace .env scanning)
load_env(Path(__file__).parent / ".env")

GATEWAY_URL = os.environ.get("GATEWAY_URL", "http://localhost:18789").rstrip("/")
# Priority: env var > local .env > gateway.cmd
Confidence
87% confidence
Finding
GATEWAY_URL is fully environment-controlled and the script sends the bearer token to whatever base URL is configured. In the intended context this tool wakes an agent session automatically from external jobs; if an attacker can influence environment variables or the local .env, they can redirect the request to an attacker-controlled server and capture the gateway token.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill declares no explicit tool scope even though its documented behavior relies on environment access, file reads, and network communication. In an agent ecosystem, missing scope declarations weaken least-privilege controls and make it easier for the skill to be invoked with broader capabilities than users expect.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The activation language is very broad, covering cron jobs, webhooks, alerts, and 'any script,' which increases the chance the skill is used in routine contexts without careful trust boundaries. That broad applicability is risky here because the payload ultimately reaches the agent as a high-trust message channel.

Ssd 3

Medium
Confidence
95% confidence
Finding
User-controlled message text is delivered directly to the agent as wake-event content, and when channel_id is provided the script appends additional operational instructions telling the agent where and how to respond. This creates an instruction-injection channel from any external script or process invoking agent-wake into the agent, which is especially risky because the skill is explicitly designed to accept asynchronous external input and cause autonomous behavior in Discord.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
The module documentation states that authentication comes from the GATEWAY_TOKEN environment variable or local .env file. However, the implementation also reads a token from ~/.openclaw/gateway.cmd via read_gateway_token_from_cmd(), which is a materially different credential source not disclosed in the docs.

Static analysis

No suspicious patterns detected.