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

Content
View full analysis

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...

text

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
View 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

Content
View full analysis

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
View 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
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 107)May include surrounding context.

python
)

    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

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 12)May include surrounding context.

python
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 18)May include surrounding context.

python
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 34)May include surrounding context.

python
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 56)May include surrounding context.

python
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 60)May include surrounding context.

python
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
---

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 72)May include surrounding context.

python
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
---

Missing User Warnings

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Ssd 1

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Ssd 1

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · scripts/agent-wake.py (reported line 57)May include surrounding context.

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

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.