mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 12:30:24 +00:00
fix(security): make allowFrom id-only by default with dangerous name opt-in (#24907)
* fix(channels): default allowFrom to id-only; add dangerous name opt-in * docs(security): align channel allowFrom docs with id-only default
This commit is contained in:
committed by
GitHub
parent
41b0568b35
commit
cfa44ea6b4
@@ -15,7 +15,7 @@ describe("slack/allow-list", () => {
|
||||
expect(normalizeSlackSlug(" #Ops.Room ")).toBe("#ops.room");
|
||||
});
|
||||
|
||||
it("matches wildcard, id, and prefixed name candidates", () => {
|
||||
it("matches wildcard and id candidates by default", () => {
|
||||
expect(resolveSlackAllowListMatch({ allowList: ["*"], id: "u1", name: "alice" })).toEqual({
|
||||
allowed: true,
|
||||
matchKey: "*",
|
||||
@@ -40,6 +40,15 @@ describe("slack/allow-list", () => {
|
||||
id: "u2",
|
||||
name: "alice",
|
||||
}),
|
||||
).toEqual({ allowed: false });
|
||||
|
||||
expect(
|
||||
resolveSlackAllowListMatch({
|
||||
allowList: ["slack:alice"],
|
||||
id: "u2",
|
||||
name: "alice",
|
||||
allowNameMatching: true,
|
||||
}),
|
||||
).toEqual({
|
||||
allowed: true,
|
||||
matchKey: "slack:alice",
|
||||
|
||||
@@ -25,6 +25,7 @@ export function resolveSlackAllowListMatch(params: {
|
||||
allowList: string[];
|
||||
id?: string;
|
||||
name?: string;
|
||||
allowNameMatching?: boolean;
|
||||
}): SlackAllowListMatch {
|
||||
const allowList = params.allowList;
|
||||
if (allowList.length === 0) {
|
||||
@@ -40,9 +41,13 @@ export function resolveSlackAllowListMatch(params: {
|
||||
{ value: id, source: "id" },
|
||||
{ value: id ? `slack:${id}` : undefined, source: "prefixed-id" },
|
||||
{ value: id ? `user:${id}` : undefined, source: "prefixed-user" },
|
||||
{ value: name, source: "name" },
|
||||
{ value: name ? `slack:${name}` : undefined, source: "prefixed-name" },
|
||||
{ value: slug, source: "slug" },
|
||||
...(params.allowNameMatching === true
|
||||
? ([
|
||||
{ value: name, source: "name" as const },
|
||||
{ value: name ? `slack:${name}` : undefined, source: "prefixed-name" as const },
|
||||
{ value: slug, source: "slug" as const },
|
||||
] satisfies Array<{ value?: string; source: SlackAllowListMatch["matchSource"] }>)
|
||||
: []),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate.value) {
|
||||
@@ -59,7 +64,12 @@ export function resolveSlackAllowListMatch(params: {
|
||||
return { allowed: false };
|
||||
}
|
||||
|
||||
export function allowListMatches(params: { allowList: string[]; id?: string; name?: string }) {
|
||||
export function allowListMatches(params: {
|
||||
allowList: string[];
|
||||
id?: string;
|
||||
name?: string;
|
||||
allowNameMatching?: boolean;
|
||||
}) {
|
||||
return resolveSlackAllowListMatch(params).allowed;
|
||||
}
|
||||
|
||||
@@ -67,6 +77,7 @@ export function resolveSlackUserAllowed(params: {
|
||||
allowList?: Array<string | number>;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
allowNameMatching?: boolean;
|
||||
}) {
|
||||
const allowList = normalizeAllowListLower(params.allowList);
|
||||
if (allowList.length === 0) {
|
||||
@@ -76,5 +87,6 @@ export function resolveSlackUserAllowed(params: {
|
||||
allowList,
|
||||
id: params.userId,
|
||||
name: params.userName,
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,14 +14,16 @@ export function isSlackSenderAllowListed(params: {
|
||||
allowListLower: string[];
|
||||
senderId: string;
|
||||
senderName?: string;
|
||||
allowNameMatching?: boolean;
|
||||
}) {
|
||||
const { allowListLower, senderId, senderName } = params;
|
||||
const { allowListLower, senderId, senderName, allowNameMatching } = params;
|
||||
return (
|
||||
allowListLower.length === 0 ||
|
||||
allowListMatches({
|
||||
allowList: allowListLower,
|
||||
id: senderId,
|
||||
name: senderName,
|
||||
allowNameMatching,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export function shouldEmitSlackReactionNotification(params: {
|
||||
userId: string;
|
||||
userName?: string | null;
|
||||
allowlist?: Array<string | number> | null;
|
||||
allowNameMatching?: boolean;
|
||||
}) {
|
||||
const { mode, botId, messageAuthorId, userId, userName, allowlist } = params;
|
||||
const effectiveMode = mode ?? "own";
|
||||
@@ -68,6 +69,7 @@ export function shouldEmitSlackReactionNotification(params: {
|
||||
allowList: users,
|
||||
id: userId,
|
||||
name: userName ?? undefined,
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -68,6 +68,7 @@ export type SlackMonitorContext = {
|
||||
dmEnabled: boolean;
|
||||
dmPolicy: DmPolicy;
|
||||
allowFrom: string[];
|
||||
allowNameMatching: boolean;
|
||||
groupDmEnabled: boolean;
|
||||
groupDmChannels: string[];
|
||||
defaultRequireMention: boolean;
|
||||
@@ -129,6 +130,7 @@ export function createSlackMonitorContext(params: {
|
||||
dmEnabled: boolean;
|
||||
dmPolicy: DmPolicy;
|
||||
allowFrom: Array<string | number> | undefined;
|
||||
allowNameMatching: boolean;
|
||||
groupDmEnabled: boolean;
|
||||
groupDmChannels: Array<string | number> | undefined;
|
||||
defaultRequireMention?: boolean;
|
||||
@@ -391,6 +393,7 @@ export function createSlackMonitorContext(params: {
|
||||
dmEnabled: params.dmEnabled,
|
||||
dmPolicy: params.dmPolicy,
|
||||
allowFrom,
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
groupDmEnabled: params.groupDmEnabled,
|
||||
groupDmChannels,
|
||||
defaultRequireMention,
|
||||
|
||||
@@ -60,6 +60,7 @@ describe("slack prepareSlackMessage inbound contract", () => {
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open",
|
||||
allowFrom: [],
|
||||
allowNameMatching: false,
|
||||
groupDmEnabled: true,
|
||||
groupDmChannels: [],
|
||||
defaultRequireMention: params.defaultRequireMention ?? true,
|
||||
|
||||
@@ -142,6 +142,7 @@ export async function prepareSlackMessage(params: {
|
||||
const allowMatch = resolveSlackAllowListMatch({
|
||||
allowList: allowFromLower,
|
||||
id: directUserId,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
});
|
||||
const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
|
||||
if (!allowMatch.allowed) {
|
||||
@@ -244,6 +245,7 @@ export async function prepareSlackMessage(params: {
|
||||
allowList: channelConfig?.users,
|
||||
userId: senderId,
|
||||
userName: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
})
|
||||
: true;
|
||||
if (isRoom && !channelUserAuthorized) {
|
||||
@@ -263,6 +265,7 @@ export async function prepareSlackMessage(params: {
|
||||
allowList: allowFromLower,
|
||||
id: senderId,
|
||||
name: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
}).allowed;
|
||||
const channelUsersAllowlistConfigured =
|
||||
isRoom && Array.isArray(channelConfig?.users) && channelConfig.users.length > 0;
|
||||
@@ -272,6 +275,7 @@ export async function prepareSlackMessage(params: {
|
||||
allowList: channelConfig?.users,
|
||||
userId: senderId,
|
||||
userName: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
})
|
||||
: false;
|
||||
const commandGate = resolveControlCommandGate({
|
||||
|
||||
@@ -77,6 +77,7 @@ const baseParams = () => ({
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open" as const,
|
||||
allowFrom: [],
|
||||
allowNameMatching: false,
|
||||
groupDmEnabled: true,
|
||||
groupDmChannels: [],
|
||||
defaultRequireMention: true,
|
||||
|
||||
@@ -210,6 +210,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
dmEnabled,
|
||||
dmPolicy,
|
||||
allowFrom,
|
||||
allowNameMatching: slackCfg.dangerouslyAllowNameMatching === true,
|
||||
groupDmEnabled,
|
||||
groupDmChannels,
|
||||
defaultRequireMention: slackCfg.requireMention,
|
||||
|
||||
@@ -361,6 +361,7 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
allowList: effectiveAllowFromLower,
|
||||
id: command.user_id,
|
||||
name: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
});
|
||||
const allowMatchMeta = formatAllowlistMatchMeta(allowMatch);
|
||||
if (!allowMatch.allowed) {
|
||||
@@ -446,6 +447,7 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
allowList: channelConfig?.users,
|
||||
userId: command.user_id,
|
||||
userName: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
})
|
||||
: false;
|
||||
if (channelUsersAllowlistConfigured && !channelUserAllowed) {
|
||||
@@ -460,6 +462,7 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
allowList: effectiveAllowFromLower,
|
||||
id: command.user_id,
|
||||
name: senderName,
|
||||
allowNameMatching: ctx.allowNameMatching,
|
||||
}).allowed;
|
||||
// DMs: allow chatting in dmPolicy=open, but keep privileged command gating intact by setting
|
||||
// CommandAuthorized based on allowlists/access-groups (downstream decides which commands need it).
|
||||
|
||||
Reference in New Issue
Block a user