Back to skill

Security audit

Async Queue

Security checks for vulnerabilities and agentic risk

Overview

This delayed-task skill is mostly transparent about what it installs, but it creates a persistent local daemon and a plugin route that can inject arbitrary queued task text into agent system events.

Install only if you are comfortable running a persistent user-level OpenClaw daemon that can wake agents and deliver queued text as system events. Review who can write ~/.openclaw/queue files and who can call authenticated local plugin routes, define allowed targets carefully, and be prepared to manually unload and remove the LaunchAgent and copied files if you stop using it.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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)

T06 · System Persistence

Error
Location
scripts/install.sh:45
Finding
Persistent LaunchAgent Runs the Queue Daemon Across User Sessions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:45-71` **Vulnerability Type**: Persistent user-level startup service **Risk Level**: High ### Vulnerable Code ```sh # ── 3. launchd plist ───────────────────────────────────────────────────────── echo " → Installing launchd plist: $PLIST" cat > "$PLIST" <<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"> <dict> <key>Label</key> <string>ai.openclaw.queue-daemon</string> <key>ProgramArguments</key> <array> <string>/usr/bin/env</string> <string>node</string> <string>${QUEUE_DIR}/daemon.js</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>${QUEUE_DIR}/daemon.log</string> <key>StandardErrorPath</key> <string>${QUEUE_DIR}/daemon.log</string> </dict> </plist> PLIST_EOF launchctl unload "$PLIST" 2>/dev/null || true launchctl load "$PLIST" ``` ### Technical Analysis The manual installer creates a user LaunchAgent in `~/Library/LaunchAgents` and immediately loads it. `RunAtLoad` causes the daemon to start when the LaunchAgent is loaded or the user logs in, while unconditional `KeepAlive` causes launchd to restart the process after it exits. Persistent execution is related to the declared delayed-task functionality and is explicitly disclosed in `SKILL.md` and `public.json`. However, unconditional restart behavior grants the component durable, cross-session execution beyond a single Skill invocation. The installed program is a JavaScript file under the user's writable home directory: ```text ~/.openclaw/queue/daemon.js ``` Consequently, any process operating as the same user that replaces this file can alter the program subsequently executed and maintained by launchd. The project also provides no automated uninstall procedure to unload and remove the service. The Launch ...[truncated 1312 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer OpenClaw's existing scheduler or another platform-managed delayed-task facility rather than installing a separate persistent daemon. 2. If launchd is required, avoid unconditional `KeepAlive`. Use an on-demand activation model, bounded execution, or a timer-based job that exits after processing. 3. Request explicit confirmation immediately before writing and loading the LaunchAgent, separately from general Skill installation consent. 4. Add a documented uninstall script that: - Uses `launchctl bootout` or the appropriate supported unload operation. - Removes the plist. - Removes the installed plugin and daemon files after user confirmation. - Preserves or securely deletes queue history according to user preference. 5. Use modern `launchctl bootstrap` and `bootout` commands with the correct per-user domain. 6. Restrict installation-directory and file permissions explicitly, and verify ownership before loading or updating the daemon. 7. Consider integrity verification or atomic, trusted update procedures so a replaced daemon file is not silently executed. 8. Clearly display the service's status, installed paths, restart policy, and removal command at installation time. ]]>

T01 · Skill Instruction Hijacking

Error
Location
plugin/index.ts:43
Finding
Untrusted Queue Task Content Is Injected into an Agent as a System Event<![CDATA[ ## Vulnerability Details **File Location**: `plugin/index.ts:43-72` **Vulnerability Type**: Privileged instruction-channel injection **Risk Level**: High ### Vulnerable Code ```ts const to = body.to ?? body.agentId; const task = body.task; if (!to || !task) { res.statusCode = 400; res.end(JSON.stringify({ error: "Missing required fields: to, task" })); return true; } // Resolve session key and agent ID // Supports both short names ("main") and full session keys ("agent:main:main") let sessionKey: string; let agentId: string; if (to.startsWith("agent:")) { // Full session key provided — e.g. "agent:main:main" sessionKey = to; const parts = to.split(":"); agentId = parts[1] ?? to; // "main" from "agent:main:main" } else { // Short agent name — e.g. "main", "myagent" agentId = to; sessionKey = `agent:${to}:main`; } // Enqueue system event so the agent sees the task in context api.runtime.system.enqueueSystemEvent( `[QUEUE:${to}] ${task}`, { sessionKey } ); ``` The task originates from unrestricted CLI text in `scripts/push.js:85-102`: ```js const item = { id: randomUUID(), to: args.to, task: args.task, runAt: runAt.toISOString(), createdAt: now.toISOString(), ttl: 300, }; if (args.then) item.then = args.then; const queuePath = path.join(__dirname, 'queue.json'); let queue = []; try { const raw = fs.readFileSync(queuePath, 'utf8'); queue = JSON.parse(raw); if (!Array.isArray(queue)) queue = []; } catch (_) { queue = []; } queue.push(item); fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2)); ``` ### Technical Analysis The plugin accepts arbitrary `task` text and forwards it directly to `enqueueSystemEvent`. Although the text is prefixed with `[QUEUE:...]`, the prefix does not isolate or ...[truncated 2444 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not place caller-controlled task text directly into a system-level instruction channel. Deliver it through an explicitly untrusted user/task event type. 2. Represent queue metadata and task content as structured fields rather than concatenating content into a system-event string. 3. Add trusted provenance metadata stating that the content is queued user data and must not override system policy or safety constraints. 4. Authorize each request against the destination session. A caller permitted to wake one agent should not automatically be able to target arbitrary session keys. 5. Use a daemon-specific, scoped authentication token and verify it in addition to general plugin authentication. 6. Validate session-key syntax and allow only configured destination agents. Do not accept arbitrary strings beginning with `agent:`. 7. Apply schema validation, type checks, and reasonable length limits to `to`, `task`, and chained-task values. 8. Restrict queue-file permissions and use atomic, locked updates to reduce same-user tampering and race conditions. 9. Require explicit user approval before queued tasks can invoke sensitive tools or perform actions unrelated to the original request. 10. Preserve an audit record containing the task origin, creator, target, creation time, and delivery authorization decision. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The skill is presented as a delayed file-backed scheduler, but the documented delivery flow includes installing a local plugin endpoint and using HTTP POSTs to trigger enqueueSystemEvent and requestHeartbeatNow on arbitrary agent targets. That expands the trust boundary from local file scheduling into an event-injection mechanism that can wake agents and deliver arbitrary task text, which is materially more powerful than the declared purpose and can enable misuse if the local endpoint or target mapping is abused.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The skill manifest describes a file-backed delayed-task scheduler for coordinating agents, emphasizing 'no infra needed.' This plugin advertises an HTTP endpoint used by a daemon to wake agents, which introduces network-facing infrastructure behavior not reflected in the stated skill description and is not an obvious requirement from that description alone.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The comment explicitly states that TARGETS comes from user config at ~/.openclaw/queue/targets.json, but TARGETS_PATH is actually built from path.dirname(QUEUE_PATH), which resolves relative to the script location. This is an active contradiction between documentation and implemented behavior, affecting where operators expect routing configuration to be sourced from.

Description-Behavior Mismatch

Medium
Confidence
83% confidence
Finding
The manifest describes a file-backed delayed task queue for coordinating agents, which suggests local queue management as the core behavior. In addition to file-backed scheduling, the code actively delivers tasks by POSTing to a localhost HTTP endpoint, a behavior not reflected in the manifest description and broader than simple queue storage/dispatch semantics.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
QUEUE_DIR="$HOME/.openclaw/queue"
PLUGIN_DIR="$HOME/.openclaw/extensions/queue-wake"
PLIST="$HOME/Library/LaunchAgents/ai.openclaw.queue-daemon.plist"

echo "🔧 Installing async-queue..."
echo ""
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.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "  → Plugin installed. Reload OpenClaw (or run: openclaw gateway restart) to activate."

# ── 3. launchd plist ─────────────────────────────────────────────────────────
echo "  → Installing launchd plist: $PLIST"
cat > "$PLIST" <<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">
Confidence
96% confidence
Finding
This duplicate finding corresponds to the same heredoc creation of the launchd plist. The risk comes from establishing automatic execution of a Node.js daemon on each login, increasing the blast radius of any later code tampering or daemon vulnerability.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "  → Plugin installed. Reload OpenClaw (or run: openclaw gateway restart) to activate."

# ── 3. launchd plist ─────────────────────────────────────────────────────────
echo "  → Installing launchd plist: $PLIST"
cat > "$PLIST" <<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">
Confidence
96% confidence
Finding
This duplicate finding corresponds to the same heredoc creation of the launchd plist. The risk comes from establishing automatic execution of a Node.js daemon on each login, increasing the blast radius of any later code tampering or daemon vulnerability.

Session Persistence

Medium
Category
Rogue Agent
Content
<string>${QUEUE_DIR}/daemon.log</string>
</dict>
</plist>
PLIST_EOF

launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"
Confidence
97% confidence
Finding
This line unloads any existing LaunchAgent before reloading, directly managing persistent execution state for the daemon. While expected for installer behavior, it reinforces session persistence and enables the daemon to remain active across logins without further user action.

Session Persistence

Medium
Category
Rogue Agent
Content
</plist>
PLIST_EOF

launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"

echo ""
Confidence
98% confidence
Finding
This line loads the LaunchAgent, immediately activating persistent execution of the queue daemon for future sessions. In the context of an agent skill, persistent background execution is more dangerous than in a simple CLI tool because it can continue processing agent tasks and any later-compromised queue data unattended.

Session Persistence

Medium
Category
Rogue Agent
Content
PLIST_EOF

launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"

echo ""
echo "✅ async-queue installed and running!"
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.

Session Persistence

Medium
Category
Rogue Agent
Content
PLIST_EOF

launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"

echo ""
echo "✅ async-queue installed and running!"
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.

Session Persistence

Medium
Category
Rogue Agent
Content
PLIST_EOF

launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"

echo ""
echo "✅ async-queue installed and running!"
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.

Static analysis

No suspicious patterns detected.