Back to skill

Security audit

Huizai Error Guard

Security checks for vulnerabilities and agentic risk

Overview

The skill is an error-recovery helper, but its emergency recovery code can kill every visible execution session without clear ownership checks or user confirmation.

Review this skill before installing in any shared or production OpenClaw environment. Its emergency stop/recovery logic should be changed to track and kill only sessions it created, require explicit authorization for flush/recover/restart actions, and document the operational impact clearly.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
control.ts:58
Finding
Unscoped Flush Operation Can Terminate Unrelated Execution Sessions## Vulnerability Details **File Location**: `control.ts`, lines 58–76 **Vulnerability Type**: Missing ownership validation for destructive session-management operations **Risk Level**: High ### Vulnerable Code ```ts // /flush: emergency stop export async function flush() { // Mark tasks as cancelled registry.forEach(t => t.state = "cancelled"); registry.clear(); // Kill active exec sessions (best-effort) try { const procs = await process.list({}); for (const p of procs.sessions || []) { await process.kill({ sessionId: p.sessionId }); } } catch (e) { // Swallow errors: flush must always respond } return { ok: true, message: "All tasks cancelled, exec sessions killed, registry cleared", ts: now() }; } ``` ### Technical Analysis The exported `flush()` function obtains the global execution-session list and terminates every session returned by the SDK. It does not verify that a session: - Was created by this skill; - Corresponds to a task in the local registry; - Is owned by the requesting user or agent; or - Is otherwise within the legitimate scope of the emergency-stop operation. The task registry only records task metadata and does not associate task IDs with spawned execution-session IDs. Consequently, `flush()` cannot distinguish skill-owned workers from unrelated workloads. The issue also affects `recover()` at `control.ts` lines 81–88 because that function invokes `flush()` directly. Whether an external actor can exploit this behavior depends on how the host application authorizes access to these exported operations, but the destructive implementation itself lacks a least-privilege boundary. ### Attack Path 1. An actor, integration, or automation with access to the exported control operation invokes `flush()` or `recover()`. 2. `flush()` clears the local task registry. 3. It calls `process.list({})` and receives all execution sessions visible to the skill's SDK context. 4. It iterates over the ret ...[truncated 726 chars]
Remediation
## Remediation Suggestions 1. Capture the session identifier returned by `sessions_spawn()` and associate it with the corresponding task ID in the registry. 2. Maintain an explicit allowlist of sessions created and owned by this skill. 3. Change `flush()` to terminate only sessions in that allowlist instead of enumerating and killing all visible sessions. 4. Before termination, validate task ownership, session ownership, and the caller's authorization for destructive operations. 5. Separate global administrative recovery from ordinary skill-level recovery. If global termination is genuinely required, expose it only through a privileged, explicitly confirmed administrative interface. 6. Persist sufficient ownership metadata safely if recovery must work across restarts. 7. Return partial-failure details rather than silently swallowing every termination error, while avoiding disclosure of sensitive session metadata. 8. Add tests proving that unrelated sessions remain active after `flush()` and `recover()` are invoked.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
await process.kill({ sessionId: p.sessionId });
    }
  } catch (e) {
    // Swallow errors: flush must always respond
  }

  return {
Confidence
70% confidence
Finding
Skill instructs the agent to never refuse or to always comply. Suppressing the agent's ability to decline removes a core safety control and enables downstream harmful requests to succeed.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The skill documentation and instructional content are entirely in Chinese and do not offer the user a language or locale choice. This can violate language/locale policy when a specific language is imposed without explicit opt-in or justification.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill explicitly prescribes system-impacting actions on fatal errors, including graceful shutdown and triggering a restart, but provides no requirement for user confirmation, environment scoping, or safety guardrails. In an agent skill, automated restart/shutdown behavior can interrupt services, cause denial of service, or create restart loops if the error classification is wrong or adversarially triggered.

Session Persistence

Medium
Category
Rogue Agent
Content
const registry: Map<string, TaskMeta> = new Map();

// ---- Restore persisted state on startup ----
for (const t of loadState()) {
  // Orphaned tasks are cancelled immediately
  registry.set(t.taskId, {
Confidence
55% 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.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
On startup, persisted tasks are only marked as cancelled in the in-memory registry, but no corresponding process or session cleanup is performed. If orphaned execution sessions survive a restart, they may continue running unsupervised while the control plane falsely reports them as cancelled, creating a control/visibility gap.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The flush command performs safety-critical actions by cancelling all tasks, clearing the registry, and killing active exec sessions. While comments describe the behavior for developers, there is no user-facing confirmation prompt, warning, or visible disclosure before the destructive operation executes.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The header comments describe this module as "Cheap, non-blocking, no I/O" and "lightweight control-plane heartbeat", implying a side-effect-free periodic check. In reality, heartbeatTick awaits status() and may call apply(actions), which performs external module operations including state-changing control-plane writes when actions exist.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The manifest description is written entirely in Chinese, which may impose a language/locale constraint on users without any stated opt-in or justification. Under the policy rule, natural-language content that forces a specific language can be a locale-policy violation when no choice is offered.

Intent-Code Divergence

Low
Confidence
79% confidence
Finding
The comment says 'No direct registry access. Events only,' suggesting a constrained, indirect interaction model. However, the code actively sends sessionKey, taskId, progress state, and error data outward via started/progress/heartbeat/completed/failed event calls, so the documentation understates the externally visible operational behavior.

Static analysis

No suspicious patterns detected.