T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- scripts/cz-agent-proxy.mjs:1108
- Finding
- Remote tool actions are automatically approved without explicit user authorization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cz-agent-proxy.mjs:1108-1152`; default enabled in `scripts/cz-agent-oneshot.mjs:154-157` and `scripts/cz-agent-proxy.mjs:11-15` **Vulnerability Type**: Automatic authorization of remote action requests **Risk Level**: High ### Vulnerable Code ```js async handleInterruptRequest(message) { if (!this.activeRequest) { return false; } if (this.interruptDecisionMode === "off") { writeEvent( createErrorEvent({ requestId: message.requestId, conversationId: message.conversationId, code: CODE_PROTOCOL_ERROR, message: "interrupt_request received but CZ_INTERRUPT_DECISION_MODE=off. Set mode to auto_approve or auto_reject.", }), ); this.clearActiveRequest(); return false; } const decisionValue = this.interruptDecisionMode === "auto_reject" ? "reject" : "approve"; const interruptDecisions = buildInterruptDecisions(message, decisionValue); const baseMetadata = asObjectOrUndefined(this.activeRequest.baseMetadata) ?? {}; const decisionMetadata = { source: "openclaw", auto_interrupt_decision: decisionValue, }; if ( Array.isArray(baseMetadata.always_allow_tools) && baseMetadata.always_allow_tools.length > 0 ) { decisionMetadata.always_allow_tools = [...baseMetadata.always_allow_tools]; } const decisionMessage = { op_type: "interrupt_decision", identity: this.activeRequest.identity, request_id: message.requestId, conversation_id: message.conversationId, interrupt_decisions: interruptDecisions, timestamp: nowMs(), metadata: decisionMetadata, }; const sent = await this.sendStudioMessage(decisionMessage); if (!sent) { this.emitActiveNetworkError("failed to send interrupt_decision to Studio WebSocket"); return false; } ``` The one-shot runner establishes the unsafe default: ```js if (!asTrimmedString(env.CZ_INTERRUPT_DECISION_MODE)) { env.CZ_INTERRUPT_D ...[truncated 2170 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Change the default decision mode to `off` or `auto_reject` in both the proxy and one-shot runner. 2. Present every interrupt request to the user with the tool name, operation, arguments, target resource, and expected effects. 3. Require a fresh, explicit user decision before sending an approval. 4. If unattended operation is necessary, implement a local allowlist covering both tool names and narrowly constrained argument patterns. 5. Reject unknown tools, malformed requests, dangerous operations, and requests outside the configured project or workspace. 6. Bind approvals to the expected request ID, conversation ID, interrupt ID, and tool-call ID, and prevent replay. 7. Record an audit event for every requested, approved, and rejected action without logging credentials or sensitive arguments. 8. Add tests confirming that missing configuration results in rejection and that remote requests cannot silently select approval mode. ]]>
