Files
openclaw/extensions/device-pair/pair-command-auth.ts
Pavan Kumar Gondhi 37c0520a0b fix(device-pair): require pairing scope for pair command [AI] (#76377)
* fix: restrict device pairing command access

* addressing review-skill

* addressing review-skill

* addressing codex review

* address codex review feedback

* addressing codex review

* addressing codex review

* addressing codex review

* addressing codex review

* docs: add changelog entry for PR merge
2026-05-04 22:12:06 +05:30

58 lines
1.6 KiB
TypeScript

type PairingCommandAuthParams = {
channel: string;
gatewayClientScopes?: readonly string[] | null;
senderIsOwner?: boolean;
};
type PairingCommandAuthState = {
isInternalGatewayCaller: boolean;
isMissingPairingPrivilege: boolean;
approvalCallerScopes?: readonly string[];
};
const COMMAND_OWNER_PAIRING_SCOPES = ["operator.pairing"] as const;
function isInternalGatewayPairingCaller(params: PairingCommandAuthParams): boolean {
return params.channel === "webchat" || Array.isArray(params.gatewayClientScopes);
}
export function resolvePairingCommandAuthState(
params: PairingCommandAuthParams,
): PairingCommandAuthState {
const isInternalGatewayCaller = isInternalGatewayPairingCaller(params);
if (isInternalGatewayCaller) {
const approvalCallerScopes = Array.isArray(params.gatewayClientScopes)
? params.gatewayClientScopes
: [];
const isMissingPairingPrivilege =
!approvalCallerScopes.includes("operator.pairing") &&
!approvalCallerScopes.includes("operator.admin");
return {
isInternalGatewayCaller,
isMissingPairingPrivilege,
approvalCallerScopes,
};
}
if (params.senderIsOwner === true) {
return {
isInternalGatewayCaller,
isMissingPairingPrivilege: false,
approvalCallerScopes: COMMAND_OWNER_PAIRING_SCOPES,
};
}
return {
isInternalGatewayCaller,
isMissingPairingPrivilege: true,
approvalCallerScopes: undefined,
};
}
export function buildMissingPairingScopeReply(): { text: string } {
return {
text: "⚠️ This command requires operator.pairing.",
};
}