mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:00:43 +00:00
fix(matrix): block DM pairing-store entries from authorizing room control commands [AI-assisted] (#67294)
* fix: address issue * fix: address review feedback * docs: add changelog entry for PR merge
This commit is contained in:
committed by
GitHub
parent
ed28df48a4
commit
f8705f512b
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- fix(matrix): block DM pairing-store entries from authorizing room control commands [AI-assisted]. (#67294) Thanks @pgondhi987.
|
||||
- Docker/build: verify `@matrix-org/matrix-sdk-crypto-nodejs` native bindings with `find` under `node_modules` instead of a hardcoded `.pnpm/...` path so pnpm v10+ virtual-store layouts no longer fail the image build. (#67143) thanks @ly85206559.
|
||||
- Matrix/E2EE: keep startup bootstrap conservative for passwordless token-auth bots, still attempt the guarded repair pass without requiring `channels.matrix.password`, and document the remaining password-UIA limitation. (#66228) Thanks @SARAMALI15792.
|
||||
- Cron/announce delivery: suppress mixed-content isolated cron announce replies that end with `NO_REPLY` so trailing silent sentinels no longer leak summary text to the target channel. (#65004) thanks @neo1027144-creator.
|
||||
|
||||
@@ -28,6 +28,25 @@ describe("resolveMatrixMonitorAccessState", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not let DM pairing-store entries authorize room control commands", () => {
|
||||
const state = resolveMatrixMonitorAccessState({
|
||||
allowFrom: [],
|
||||
storeAllowFrom: ["@attacker:example.org"],
|
||||
groupAllowFrom: [],
|
||||
roomUsers: [],
|
||||
senderId: "@attacker:example.org",
|
||||
isRoom: true,
|
||||
});
|
||||
|
||||
expect(state.effectiveAllowFrom).toEqual(["@attacker:example.org"]);
|
||||
expect(state.directAllowMatch.allowed).toBe(true);
|
||||
expect(state.commandAuthorizers).toEqual([
|
||||
{ configured: false, allowed: false },
|
||||
{ configured: false, allowed: false },
|
||||
{ configured: false, allowed: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps room-user matching disabled for dm traffic", () => {
|
||||
const state = resolveMatrixMonitorAccessState({
|
||||
allowFrom: [],
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { normalizeMatrixAllowList, resolveMatrixAllowListMatch } from "./allowlist.js";
|
||||
import type { MatrixAllowListMatch } from "./allowlist.js";
|
||||
|
||||
type MatrixCommandAuthorizer = {
|
||||
configured: boolean;
|
||||
allowed: boolean;
|
||||
};
|
||||
|
||||
type MatrixMonitorAllowListMatch = {
|
||||
allowed: boolean;
|
||||
matchKey?: string;
|
||||
matchSource?: "wildcard" | "id" | "prefixed-id" | "prefixed-user";
|
||||
};
|
||||
|
||||
export type MatrixMonitorAccessState = {
|
||||
effectiveAllowFrom: string[];
|
||||
effectiveGroupAllowFrom: string[];
|
||||
effectiveRoomUsers: string[];
|
||||
groupAllowConfigured: boolean;
|
||||
directAllowMatch: MatrixAllowListMatch;
|
||||
roomUserMatch: MatrixAllowListMatch | null;
|
||||
groupAllowMatch: MatrixAllowListMatch | null;
|
||||
directAllowMatch: MatrixMonitorAllowListMatch;
|
||||
roomUserMatch: MatrixMonitorAllowListMatch | null;
|
||||
groupAllowMatch: MatrixMonitorAllowListMatch | null;
|
||||
commandAuthorizers: [MatrixCommandAuthorizer, MatrixCommandAuthorizer, MatrixCommandAuthorizer];
|
||||
};
|
||||
|
||||
@@ -25,12 +30,14 @@ export function resolveMatrixMonitorAccessState(params: {
|
||||
senderId: string;
|
||||
isRoom: boolean;
|
||||
}): MatrixMonitorAccessState {
|
||||
const configuredAllowFrom = normalizeMatrixAllowList(params.allowFrom);
|
||||
const effectiveAllowFrom = normalizeMatrixAllowList([
|
||||
...params.allowFrom,
|
||||
...configuredAllowFrom,
|
||||
...params.storeAllowFrom,
|
||||
]);
|
||||
const effectiveGroupAllowFrom = normalizeMatrixAllowList(params.groupAllowFrom);
|
||||
const effectiveRoomUsers = normalizeMatrixAllowList(params.roomUsers);
|
||||
const commandAllowFrom = params.isRoom ? configuredAllowFrom : effectiveAllowFrom;
|
||||
|
||||
const directAllowMatch = resolveMatrixAllowListMatch({
|
||||
allowList: effectiveAllowFrom,
|
||||
@@ -50,6 +57,13 @@ export function resolveMatrixMonitorAccessState(params: {
|
||||
userId: params.senderId,
|
||||
})
|
||||
: null;
|
||||
const commandAllowMatch =
|
||||
commandAllowFrom.length > 0
|
||||
? resolveMatrixAllowListMatch({
|
||||
allowList: commandAllowFrom,
|
||||
userId: params.senderId,
|
||||
})
|
||||
: null;
|
||||
|
||||
return {
|
||||
effectiveAllowFrom,
|
||||
@@ -61,8 +75,8 @@ export function resolveMatrixMonitorAccessState(params: {
|
||||
groupAllowMatch,
|
||||
commandAuthorizers: [
|
||||
{
|
||||
configured: effectiveAllowFrom.length > 0,
|
||||
allowed: directAllowMatch.allowed,
|
||||
configured: commandAllowFrom.length > 0,
|
||||
allowed: commandAllowMatch?.allowed ?? false,
|
||||
},
|
||||
{
|
||||
configured: effectiveRoomUsers.length > 0,
|
||||
|
||||
@@ -445,6 +445,36 @@ describe("matrix monitor handler pairing account scope", () => {
|
||||
expect(recordInboundSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("blocks room control commands from DM-only paired senders", async () => {
|
||||
const { handler, finalizeInboundContext, recordInboundSession } =
|
||||
createMatrixHandlerTestHarness({
|
||||
isDirectMessage: false,
|
||||
readAllowFromStore: vi.fn(async () => ["@user:example.org"]),
|
||||
roomsConfig: {
|
||||
"!room:example.org": { requireMention: false },
|
||||
},
|
||||
shouldHandleTextCommands: () => true,
|
||||
hasControlCommand: () => true,
|
||||
cfg: {
|
||||
commands: {
|
||||
useAccessGroups: true,
|
||||
},
|
||||
},
|
||||
getMemberDisplayName: async () => "sender",
|
||||
});
|
||||
|
||||
await handler(
|
||||
"!room:example.org",
|
||||
createMatrixTextMessageEvent({
|
||||
eventId: "$dm-only-room-command",
|
||||
body: "/config",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(recordInboundSession).not.toHaveBeenCalled();
|
||||
expect(finalizeInboundContext).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("processes room messages mentioned via displayName in formatted_body", async () => {
|
||||
const recordInboundSession = vi.fn(async () => {});
|
||||
const { handler } = createMatrixHandlerTestHarness({
|
||||
|
||||
Reference in New Issue
Block a user