openclaw - 💡(How to fix) Fix [Security]: /pair approve bypasses admin scope guard via plugin command path [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#56173Fetched 2026-04-08 01:44:09
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1

The /pair approve slash command in the device-pair plugin bypasses the admin-scope authorization guard that is correctly enforced on the RPC device-pair approval path.

Root Cause

approveDevicePairing() in src/infra/device-pairing.ts guards admin-scope approvals behind an options?.callerScopes check (line 471). When callerScopes is not provided, the guard is skipped entirely:

if (approvalRole && options?.callerScopes) {
  // authorization check runs here
}

The RPC handler in src/gateway/server-methods/devices.ts:94 correctly passes callerScopes to this function, which is validated by src/gateway/server.device-pair-approve-authz.test.ts:92 — pairing-scoped callers are rejected when approving admin-scope requests.

However, the plugin command handler calls:

const approved = await approveDevicePairing(pending.requestId);

without forwarding ctx.gatewayClientScopes, so callerScopes is undefined and the guard never fires.

Code Example

if (approvalRole && options?.callerScopes) {
  // authorization check runs here
}

---

const approved = await approveDevicePairing(pending.requestId);
RAW_BUFFERClick to expand / collapse

Security Vulnerability — Auth Bypass in device-pair Plugin

Component

extensions/device-pair/index.ts/pair approve command handler (line ~614)

Description

The /pair approve slash command in the device-pair plugin bypasses the admin-scope authorization guard that is correctly enforced on the RPC device-pair approval path.

Root Cause

approveDevicePairing() in src/infra/device-pairing.ts guards admin-scope approvals behind an options?.callerScopes check (line 471). When callerScopes is not provided, the guard is skipped entirely:

if (approvalRole && options?.callerScopes) {
  // authorization check runs here
}

The RPC handler in src/gateway/server-methods/devices.ts:94 correctly passes callerScopes to this function, which is validated by src/gateway/server.device-pair-approve-authz.test.ts:92 — pairing-scoped callers are rejected when approving admin-scope requests.

However, the plugin command handler calls:

const approved = await approveDevicePairing(pending.requestId);

without forwarding ctx.gatewayClientScopes, so callerScopes is undefined and the guard never fires.

Impact

An operator session with only operator.pairing scope (but without operator.admin) can approve a pending device-pairing request that requests operator.admin scopes via the /pair approve chat command. This escalates the operator to full admin capability on the target node, bypassing the RPC layer's intentional authorization check.

Reproduction

  1. Enable the device-pair plugin on a gateway with an operator that has operator.write + operator.pairing but NOT operator.admin.
  2. Create a pending device-pairing request requesting role: "operator" and scopes: ["operator.admin"].
  3. From the operator's chat session, send /pair approve latest.
  4. The pairing is approved; the device receives an admin token.
  5. The same approval via device.pair.approve RPC is correctly rejected with missing scope: operator.admin.

Suggested Fix

Thread ctx.gatewayClientScopes as callerScopes through the /pair approve handler into approveDevicePairing(), and add a test replicating the authorization test already present for the RPC path.

Disclosure

This is a real security bypass. Please treat as a security advisory. Happy to follow responsible disclosure process if preferred.

extent analysis

Fix Plan

To fix the security vulnerability, we need to pass ctx.gatewayClientScopes as callerScopes to the approveDevicePairing() function. Here are the steps:

  • Update the /pair approve command handler in extensions/device-pair/index.ts to pass ctx.gatewayClientScopes:
const approved = await approveDevicePairing(pending.requestId, {
  callerScopes: ctx.gatewayClientScopes,
});
  • Update the approveDevicePairing() function in src/infra/device-pairing.ts to accept the callerScopes option:
async function approveDevicePairing(requestId: string, options: { callerScopes?: string[] } = {}) {
  // ...
}
  • Add a test to replicate the authorization test already present for the RPC path:
it('should reject pairing approval without admin scope', async () => {
  const ctx = { gatewayClientScopes: ['operator.pairing'] };
  const pending = { requestId: 'test-request-id', role: 'operator', scopes: ['operator.admin'] };
  await expect(approveDevicePairing(pending.requestId, { callerScopes: ctx.gatewayClientScopes })).rejects.toThrow('missing scope: operator.admin');
});

Verification

To verify that the fix worked, follow the reproduction steps and ensure that the pairing approval is rejected when the operator does not have the operator.admin scope.

Extra Tips

  • Make sure to update the documentation and notify relevant teams about the security fix.
  • Consider adding additional logging and monitoring to detect similar security vulnerabilities in the future.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING