fix: enforce explicit group auth boundaries across channels

This commit is contained in:
Peter Steinberger
2026-02-26 18:15:57 +01:00
parent d0d83a2020
commit 64de4b6d6a
20 changed files with 614 additions and 331 deletions

View File

@@ -9,12 +9,19 @@ import {
import { resolveSlackChannelConfig } from "./channel-config.js";
import { normalizeSlackChannelType, type SlackMonitorContext } from "./context.js";
export async function resolveSlackEffectiveAllowFrom(ctx: SlackMonitorContext) {
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "slack",
dmPolicy: ctx.dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
export async function resolveSlackEffectiveAllowFrom(
ctx: SlackMonitorContext,
options?: { includePairingStore?: boolean },
) {
const includePairingStore = options?.includePairingStore === true;
const storeAllowFrom =
includePairingStore
? await readStoreAllowFromForDmPolicy({
provider: "slack",
dmPolicy: ctx.dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
})
: [];
const allowFrom = normalizeAllowList([...ctx.allowFrom, ...storeAllowFrom]);
const allowFromLower = normalizeAllowListLower(allowFrom);
return { allowFrom, allowFromLower };
@@ -99,15 +106,15 @@ export async function authorizeSlackSystemEventSender(params: {
.catch(() => ({}));
const senderName = senderInfo.name;
const resolveAllowFromLower = async () =>
(await resolveSlackEffectiveAllowFrom(params.ctx)).allowFromLower;
const resolveAllowFromLower = async (includePairingStore = false) =>
(await resolveSlackEffectiveAllowFrom(params.ctx, { includePairingStore })).allowFromLower;
if (channelType === "im") {
if (!params.ctx.dmEnabled || params.ctx.dmPolicy === "disabled") {
return { allowed: false, reason: "dm-disabled", channelType, channelName };
}
if (params.ctx.dmPolicy !== "open") {
const allowFromLower = await resolveAllowFromLower();
const allowFromLower = await resolveAllowFromLower(true);
const senderAllowListed = isSlackSenderAllowListed({
allowListLower: allowFromLower,
senderId,
@@ -126,7 +133,7 @@ export async function authorizeSlackSystemEventSender(params: {
} else if (!channelId) {
// No channel context. Apply allowFrom if configured so we fail closed
// for privileged interactive events when owner allowlist is present.
const allowFromLower = await resolveAllowFromLower();
const allowFromLower = await resolveAllowFromLower(false);
if (allowFromLower.length > 0) {
const senderAllowListed = isSlackSenderAllowListed({
allowListLower: allowFromLower,

View File

@@ -127,7 +127,9 @@ export async function prepareSlackMessage(params: {
return null;
}
const { allowFromLower } = await resolveSlackEffectiveAllowFrom(ctx);
const { allowFromLower } = await resolveSlackEffectiveAllowFrom(ctx, {
includePairingStore: isDirectMessage,
});
if (isDirectMessage) {
const directUserId = message.user;

View File

@@ -336,11 +336,14 @@ export async function registerSlackMonitorSlashCommands(params: {
return;
}
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "slack",
dmPolicy: ctx.dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
const storeAllowFrom =
isDirectMessage
? await readStoreAllowFromForDmPolicy({
provider: "slack",
dmPolicy: ctx.dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
})
: [];
const effectiveAllowFrom = normalizeAllowList([...ctx.allowFrom, ...storeAllowFrom]);
const effectiveAllowFromLower = normalizeAllowListLower(effectiveAllowFrom);