Back to skill

Security audit

JEP Guard

Security checks for vulnerabilities and agentic risk

Overview

This security skill is not clearly malicious, but it asks for high-impact control and its advertised protection can silently fail open.

Review this before installing as an active security module. Passive mode is lower risk, but full mode grants broad local interception/audit authority while the implementation can silently allow operations if the daemon is unavailable or errors. Do not rely on it as a fail-closed enforcement boundary until the IPC, identity binding, and error handling are fixed.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
openclaw.hooks.js:87
Finding
Execution protection fails open when the guard daemon is unavailable or returns an error<![CDATA[ ## Vulnerability Details **File Location**: `openclaw.hooks.js:87-120`, `src/daemon/server.ts:85-93`, `src/core/jep-core.ts:50-53` **Vulnerability Type**: Fail-open security control and inconsistent event identity validation **Risk Level**: High ### Vulnerable Code `openclaw.hooks.js:87-120`: ```javascript exports.preExec = async function(command, context) { if (!isFullMode()) return command; if (!fs.existsSync(GUARD_SOCKET)) return command; try { const result = await guardCall('JUDGE', context.skillId, { action: command.action, target: command.target, context: { args: command.args, cwd: command.cwd } }); if (result.action === 'block') { const err = new Error(`JEP Guard blocked: ${result.reason}`); err.code = 'JEP_BLOCKED'; throw err; } return { ...command, _jep: { token: result.capabilityToken, eventId: result.event?.nonce, granted: true } }; } catch (err) { if (err.code === 'JEP_BLOCKED') throw err; return command; } }; ``` `src/daemon/server.ts:85-93`: ```typescript case 'JUDGE': return this.gate.process({ requester: req.skill, action: req.payload.action, target: req.payload.target || '', type: 'system_call', context: req.payload.context }); ``` `src/core/jep-core.ts:50-53`: ```typescript createJudge(payload: unknown, agent?: string, predecessors?: string[]): JPEvent { if (agent && agent !== this.agentId) throw new Error('Agent mismatch'); return this.createEvent('J', payload, predecessors); } ``` ### Technical Analysis The daemon constructs its `JEPCore` instance with the fixed identity `jep-guard-daemon`, while judgment requests identify the requesting skill through `req.skill`. `CausalGateService.process()` passes that requester identity to `createJudge()`. Unless the requesting skill is literally named `jep-guard-daemon`, `createJudge()` rejects the request with `Agent mismatch`. The hook treats every error other th ...[truncated 2035 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Separate the event signer from the event subject: - Keep `jep-guard-daemon` as the cryptographic signer. - Store the requesting skill as an explicit, validated event field rather than passing it as the core agent identity. - Alternatively, construct and verify a dedicated identity context for each authenticated skill. 2. Fail closed for protected operations: - If full-protection mode is active, deny high-risk operations when the daemon is unavailable, times out, returns malformed JSON, or returns an unknown decision. - Permit fail-open behavior only through an explicit, documented configuration option with clear warnings. - Never interpret an error object or missing `action` field as approval. 3. Validate daemon responses: - Require `action` to be one of `allow`, `block`, or `review`. - Require a valid signed capability token before returning an allowed command. - Treat `review` as blocked until the required review is completed. 4. Harden local IPC: - Create the socket in a private directory owned by the current user rather than using a predictable path directly under the shared temporary directory. - Verify socket ownership and type before connecting. - Use authenticated requests or peer-credential validation where supported. - Handle stale sockets without silently disabling protection. 5. Add integration tests covering: - A normal registered skill judgment. - Agent identity consistency. - Missing daemon and missing socket. - Timeout and malformed response behavior. - Unknown or incomplete daemon decisions. - Explicit block propagation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/daemon/registry.ts:19
Finding
Skill registration creates unusable identities without proof of possession<![CDATA[ ## Vulnerability Details **File Location**: `src/daemon/registry.ts:19-26`, `src/daemon/registry.ts:56-62` **Vulnerability Type**: Broken skill identity binding and ineffective signature authentication **Risk Level**: Medium ### Vulnerable Code `src/daemon/registry.ts:19-26`: ```typescript register(manifest: Partial<SkillIdentity> & { name: string; version: string }): SkillIdentity { const keyPair = nacl.sign.keyPair(); const skill: SkillIdentity = { skill_id: manifest.name, name: manifest.name, version: manifest.version, pubkey: Buffer.from(keyPair.publicKey).toString('base64'), capabilities: manifest.capabilities || [], ``` `src/daemon/registry.ts:56-62`: ```typescript verifySignature(skillId: string, message: string, signature: string): boolean { const skill = this.skills.get(skillId); if (!skill) return false; const msgBytes = Buffer.from(message, 'utf-8'); const sigBytes = Buffer.from(signature, 'base64'); const pubBytes = Buffer.from(skill.pubkey, 'base64'); return nacl.sign.detached.verify(msgBytes, sigBytes, pubBytes); } ``` ### Technical Analysis Registration generates a new Ed25519 keypair internally but stores only the public key. The corresponding secret key is neither returned to the skill nor securely retained for an identity service. As a result, the registered skill cannot generate a signature that `verifySignature()` will accept. Registration also accepts a manifest name and capabilities without proof that the caller controls the named skill. The generated random key does not solve that problem because no proof-of-possession exchange occurs and the key is not bound to an authenticated runtime identity. Furthermore, the daemon's registration request path does not require a signature before establishing the registry entry. Although the Unix socket is changed to mode `0600`, any process running under the same operating-system user may potentially submit registration requests. This is a meanin ...[truncated 1974 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use caller-generated identities: - Require each skill to provide its public key during registration. - Require a signature over a canonical registration challenge containing the skill ID, version, capabilities, nonce, and timestamp. - Verify proof of possession before creating or replacing a registry entry. 2. Bind registration to runtime identity: - Obtain the authoritative skill identifier and manifest from OpenClaw rather than trusting request fields. - Authenticate IPC clients using operating-system peer credentials or a runtime-issued credential. - Reject attempts to register a name that does not match the authenticated caller. 3. Protect registry updates: - Do not silently overwrite an existing skill identity. - Require a signed key-rotation statement from the previous key, or an explicit trusted-administrator recovery flow. - Validate capability declarations against the authoritative installed manifest. 4. Harden signature verification: - Validate decoded public-key and signature lengths before calling TweetNaCl. - Catch malformed encoding errors and return a controlled authentication failure. - Canonicalize signed messages consistently and include anti-replay fields. 5. Add tests for: - Successful proof-of-possession registration. - Rejection of impersonated skill names. - Duplicate registration and key rotation. - Malformed Base64 and invalid key lengths. - Registration attempts from unauthenticated local clients. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (38)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case 'keep':
      console.log('\n✅ Config preserved.');
      console.log('   To restart: claw install jep-guard');
      console.log('   To delete later: rm -rf ~/.jep-guard\n');
      break;

    case '2':
Confidence
100% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case 'keep':
      console.log('\n✅ Config preserved.');
      console.log('   To restart: claw install jep-guard');
      console.log('   To delete later: rm -rf ~/.jep-guard\n');
      break;

    case '2':
Confidence
95% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
case 'keep':
      console.log('\n✅ Config preserved.');
      console.log('   To restart: claw install jep-guard');
      console.log('   To delete later: rm -rf ~/.jep-guard\n');
      break;

    case '2':
Confidence
100% 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).

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The package description explicitly claims "zero auto-execution" and "zero shell commands during install," yet the manifest defines a postinstall hook that executes code automatically during installation. Even though it invokes Node rather than a shell directly, it still creates install-time code execution and a trust-boundary violation, which is especially concerning because the metadata appears to downplay that behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
// SECURITY: Always show privilege requirements upfront
  console.log(`${colors.yellow}REQUIRED PRIVILEGES:${colors.reset}`);
  console.log(`${colors.gray}  · Intercept skill executions (preExec hooks)${colors.reset}`);
  console.log(`${colors.gray}  · Write audit logs to ~/.jep-guard/${colors.reset}`);
  console.log(`${colors.gray}  · Run persistent background daemon (MANUAL START ONLY)${colors.reset}`);
  console.log(`${colors.gray}  · Modify OpenClaw runtime security settings${colors.reset}`);
  console.log('');
Confidence
60% 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
// v2.0.4: Explicit security notice
  console.log(`${colors.yellow}SECURITY NOTICE (v2.0.4):${colors.reset}`);
  console.log(`${colors.gray}  · Installer will NEVER spawn background processes${colors.reset}`);
  console.log(`${colors.gray}  · Installer will NEVER execute shell commands${colors.reset}`);
  console.log(`${colors.gray}  · You must manually start the daemon after installation${colors.reset}`);
  console.log('');
Confidence
70% 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
## Security Notes (v2.0.4)

- Installer does **not** auto-start background processes.
- No shell commands are executed during install/uninstall.
- Full-protection mode requires explicit interactive consent.
Confidence
70% 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.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The manifest markets the skill as a broad 'safety layer' for multi-agent systems and describes extensive protection, audit, delegation, and reputation functions without clearly constraining when and how it should be invoked. In a skill that also requests process control, file writes, signaling, local IPC, hooks, and a daemon entrypoint, ambiguous scope increases the risk of overbroad activation and operator misunderstanding about what the skill can intercept or modify.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## What you get

  - 🛡️ **Invisible Protection**: 95% of operations auto-approved,
    only anomalies trigger alerts
  - 📊 **Complete Audit**: Every judgment, delegation, and
    verification recorded in JEP standard format
Confidence
88% confidence
Finding
The phrase '95% of operations auto-approved' indicates autonomous security decisions with limited human review. In the context of a skill that claims to gate execution and intercept subprocesses, automatic approvals can weaken safeguards, create silent allow paths, and make dangerous behavior harder for users to notice or challenge.

Session Persistence

Medium
Category
Rogue Agent
Content
# Capabilities we need from the runtime
capabilities:
  - system:process_control    # Intercept subprocesses
  - system:file_write         # Write audit logs
  - system:network_local      # Local IPC socket
  - system:signal             # Signal handling
Confidence
81% confidence
Finding
The skill requests capabilities and declares behavior consistent with persistent monitoring: process interception, file writes for audit logs, local IPC, signal handling, a persistent daemon, and cross-skill security services. Even though the manifest says daemon startup requires consent, the overall design introduces durable session/state persistence that could enable long-lived monitoring, control, or data retention if misused or implemented unsafely.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
runtime_modification:
    description: Modifies OpenClaw security.module config when user explicitly enables full mode
    requires_consent: true
    auto_execute: false
  persistent_daemon:
    description: Runs background daemon process for skill interception
    requires_consent: true
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The command queries audit events using the provided since/agent filters, but the export operation ignores those filtered results and instead exports from the service directly. This can cause users to believe they are exporting a narrowed dataset while actually writing a broader audit stream, which may leak unrelated or sensitive audit records to stdout or a file.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The service appends every audit event to a file under the user's home directory, which is a persistent write of potentially sensitive activity data. There is no confirmation prompt, user-facing log message, or explanatory comment/docstring warning that audit data will be stored on disk.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code opens a Unix domain socket connection and immediately sends a JSON-serialized message containing the delegated payload and requester identity. There is no confirmation prompt, user-facing log, or explanatory comment/docstring near the transmission, so users are not warned that their data is being forwarded to another local service.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The extension forwards request.action, request.target, and request.context to a global external kernel and separately forwards event metadata to its audit interface without any consent gate, minimization, or trust validation. Because the recipient is a mutable global object, any code able to populate or replace globalThis.aegisKernel can exfiltrate potentially sensitive operational or user data, making the issue more dangerous than a purely internal logging path.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code opens a socket connection and transmits a JSON request containing the method, skill identifier, and payload, which may include user or system data. There is no confirmation prompt, logging, comment, or docstring in this file disclosing that outbound IPC/network-style communication occurs.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"postinstall": "node scripts/post-install.js"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "inquirer": "^9.2.0",
    "tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "commander": "^12.0.0",
    "inquirer": "^9.2.0",
    "tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "commander": "^12.0.0",
    "inquirer": "^9.2.0",
    "tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
    "chalk": "^5.3.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"commander": "^12.0.0",
    "inquirer": "^9.2.0",
    "tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
    "chalk": "^5.3.0",
    "ora": "^8.0.1",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: uuid has 1 known advisory(ies) (CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"inquirer": "^9.2.0",
    "tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
    "chalk": "^5.3.0",
    "ora": "^8.0.1",
    "tar": "^6.2.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"tweetnacl": "^1.0.3",
    "uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
    "chalk": "^5.3.0",
    "ora": "^8.0.1",
    "tar": "^6.2.0"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unverifiable Dependency: chalk has 1 known advisory(ies) (MAL-2025-46969 (Malicious code in chalk (npm))), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"uuid": "^9.0.0",
    "fast-json-stable-stringify": "^2.1.0",
    "chalk": "^5.3.0",
    "ora": "^8.0.1",
    "tar": "^6.2.0"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.