fix(msteams): enforce sender allowlists with route allowlists

This commit is contained in:
Peter Steinberger
2026-03-09 05:51:57 +00:00
parent 03a6e3b460
commit 88aee9161e
3 changed files with 69 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
### Fixes ### Fixes
- Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent. - Browser/SSRF: block private-network intermediate redirect hops in strict browser navigation flows and fail closed when remote tab-open paths cannot inspect redirect chains. Thanks @zpbrent.
- MS Teams/authz: keep `groupPolicy: "allowlist"` enforcing sender allowlists even when a team/channel route allowlist is configured, so route matches no longer widen group access to every sender in that route. Thanks @zpbrent.
## 2026.3.8 ## 2026.3.8

View File

@@ -5,7 +5,7 @@ import { setMSTeamsRuntime } from "../runtime.js";
import { createMSTeamsMessageHandler } from "./message-handler.js"; import { createMSTeamsMessageHandler } from "./message-handler.js";
describe("msteams monitor handler authz", () => { describe("msteams monitor handler authz", () => {
it("does not treat DM pairing-store entries as group allowlist entries", async () => { function createDeps(cfg: OpenClawConfig) {
const readAllowFromStore = vi.fn(async () => ["attacker-aad"]); const readAllowFromStore = vi.fn(async () => ["attacker-aad"]);
setMSTeamsRuntime({ setMSTeamsRuntime({
logging: { shouldLogVerbose: () => false }, logging: { shouldLogVerbose: () => false },
@@ -35,16 +35,7 @@ describe("msteams monitor handler authz", () => {
}; };
const deps: MSTeamsMessageHandlerDeps = { const deps: MSTeamsMessageHandlerDeps = {
cfg: { cfg,
channels: {
msteams: {
dmPolicy: "pairing",
allowFrom: [],
groupPolicy: "allowlist",
groupAllowFrom: [],
},
},
} as OpenClawConfig,
runtime: { error: vi.fn() } as unknown as RuntimeEnv, runtime: { error: vi.fn() } as unknown as RuntimeEnv,
appId: "test-app", appId: "test-app",
adapter: {} as MSTeamsMessageHandlerDeps["adapter"], adapter: {} as MSTeamsMessageHandlerDeps["adapter"],
@@ -65,6 +56,21 @@ describe("msteams monitor handler authz", () => {
} as unknown as MSTeamsMessageHandlerDeps["log"], } as unknown as MSTeamsMessageHandlerDeps["log"],
}; };
return { conversationStore, deps, readAllowFromStore };
}
it("does not treat DM pairing-store entries as group allowlist entries", async () => {
const { conversationStore, deps, readAllowFromStore } = createDeps({
channels: {
msteams: {
dmPolicy: "pairing",
allowFrom: [],
groupPolicy: "allowlist",
groupAllowFrom: [],
},
},
} as OpenClawConfig);
const handler = createMSTeamsMessageHandler(deps); const handler = createMSTeamsMessageHandler(deps);
await handler({ await handler({
activity: { activity: {
@@ -96,4 +102,54 @@ describe("msteams monitor handler authz", () => {
}); });
expect(conversationStore.upsert).not.toHaveBeenCalled(); expect(conversationStore.upsert).not.toHaveBeenCalled();
}); });
it("does not widen sender auth when only a teams route allowlist is configured", async () => {
const { conversationStore, deps } = createDeps({
channels: {
msteams: {
dmPolicy: "pairing",
allowFrom: [],
groupPolicy: "allowlist",
groupAllowFrom: [],
teams: {
team123: {
channels: {
"19:group@thread.tacv2": { requireMention: false },
},
},
},
},
},
} as OpenClawConfig);
const handler = createMSTeamsMessageHandler(deps);
await handler({
activity: {
id: "msg-1",
type: "message",
text: "hello",
from: {
id: "attacker-id",
aadObjectId: "attacker-aad",
name: "Attacker",
},
recipient: {
id: "bot-id",
name: "Bot",
},
conversation: {
id: "19:group@thread.tacv2",
conversationType: "groupChat",
},
channelData: {
team: { id: "team123", name: "Team 123" },
channel: { name: "General" },
},
attachments: [],
},
sendActivity: vi.fn(async () => undefined),
} as unknown as Parameters<typeof handler>[0]);
expect(conversationStore.upsert).not.toHaveBeenCalled();
});
}); });

View File

@@ -242,10 +242,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
} }
const senderGroupAccess = evaluateSenderGroupAccessForPolicy({ const senderGroupAccess = evaluateSenderGroupAccessForPolicy({
groupPolicy, groupPolicy,
groupAllowFrom: groupAllowFrom: effectiveGroupAllowFrom,
effectiveGroupAllowFrom.length > 0 || !channelGate.allowlistConfigured
? effectiveGroupAllowFrom
: ["*"],
senderId, senderId,
isSenderAllowed: (_senderId, allowFrom) => isSenderAllowed: (_senderId, allowFrom) =>
resolveMSTeamsAllowlistMatch({ resolveMSTeamsAllowlistMatch({