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