refactor(security): enforce account-scoped pairing APIs

This commit is contained in:
Peter Steinberger
2026-02-26 21:57:10 +01:00
parent a0c5e28f3b
commit bce643a0bd
27 changed files with 331 additions and 94 deletions

View File

@@ -35,10 +35,7 @@ import { logVerbose } from "../../globals.js";
import { enqueueSystemEvent } from "../../infra/system-events.js";
import { logDebug, logError } from "../../logger.js";
import { buildPairingReply } from "../../pairing/pairing-messages.js";
import {
readChannelAllowFromStore,
upsertChannelPairingRequest,
} from "../../pairing/pairing-store.js";
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js";
import { readStoreAllowFromForDmPolicy } from "../../security/dm-policy-shared.js";
@@ -474,8 +471,8 @@ async function ensureDmComponentAuthorized(params: {
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "discord",
accountId: ctx.accountId,
dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
const effectiveAllowFrom = [...(ctx.allowFrom ?? []), ...storeAllowFrom];
const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
@@ -498,6 +495,7 @@ async function ensureDmComponentAuthorized(params: {
const { code, created } = await upsertChannelPairingRequest({
channel: "discord",
id: user.id,
accountId: ctx.accountId,
meta: {
tag: formatDiscordUserTag(user),
name: user.username,

View File

@@ -11,7 +11,6 @@ import { danger, logVerbose } from "../../globals.js";
import { formatDurationSeconds } from "../../infra/format-time/format-duration.ts";
import { enqueueSystemEvent } from "../../infra/system-events.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { readChannelAllowFromStore } from "../../pairing/pairing-store.js";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import {
readStoreAllowFromForDmPolicy,
@@ -208,6 +207,7 @@ async function runDiscordReactionHandler(params: {
}
type DiscordReactionIngressAuthorizationParams = {
accountId: string;
user: User;
isDirectMessage: boolean;
isGroupDm: boolean;
@@ -238,8 +238,8 @@ async function authorizeDiscordReactionIngress(
if (params.isDirectMessage) {
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "discord",
accountId: params.accountId,
dmPolicy: params.dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
const access = resolveDmGroupAccessWithLists({
isGroup: false,
@@ -358,6 +358,7 @@ async function handleDiscordReactionEvent(params: {
channelType === ChannelType.PrivateThread ||
channelType === ChannelType.AnnouncementThread;
const ingressAccess = await authorizeDiscordReactionIngress({
accountId: params.accountId,
user,
isDirectMessage,
isGroupDm,
@@ -486,6 +487,7 @@ async function handleDiscordReactionEvent(params: {
const channelConfig = resolveThreadChannelConfig();
const threadAccess = await authorizeDiscordReactionIngress({
accountId: params.accountId,
user,
isDirectMessage,
isGroupDm,
@@ -528,6 +530,7 @@ async function handleDiscordReactionEvent(params: {
const channelConfig = resolveThreadChannelConfig();
const threadAccess = await authorizeDiscordReactionIngress({
accountId: params.accountId,
user,
isDirectMessage,
isGroupDm,
@@ -571,6 +574,7 @@ async function handleDiscordReactionEvent(params: {
});
if (isGuildMessage) {
const channelAccess = await authorizeDiscordReactionIngress({
accountId: params.accountId,
user,
isDirectMessage,
isGroupDm,

View File

@@ -25,12 +25,9 @@ import { enqueueSystemEvent } from "../../infra/system-events.js";
import { logDebug } from "../../logger.js";
import { getChildLogger } from "../../logging.js";
import { buildPairingReply } from "../../pairing/pairing-messages.js";
import {
readChannelAllowFromStore,
upsertChannelPairingRequest,
} from "../../pairing/pairing-store.js";
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import { DEFAULT_ACCOUNT_ID, resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import { readStoreAllowFromForDmPolicy } from "../../security/dm-policy-shared.js";
import { fetchPluralKitMessageInfo } from "../pluralkit.js";
import { sendMessageDiscord } from "../send.js";
@@ -177,6 +174,7 @@ export async function preflightDiscordMessage(
}
const dmPolicy = params.discordConfig?.dmPolicy ?? params.discordConfig?.dm?.policy ?? "pairing";
const resolvedAccountId = params.accountId ?? DEFAULT_ACCOUNT_ID;
let commandAuthorized = true;
if (isDirectMessage) {
if (dmPolicy === "disabled") {
@@ -186,8 +184,8 @@ export async function preflightDiscordMessage(
if (dmPolicy !== "open") {
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "discord",
accountId: resolvedAccountId,
dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
const effectiveAllowFrom = [...(params.allowFrom ?? []), ...storeAllowFrom];
const allowList = normalizeDiscordAllowList(effectiveAllowFrom, ["discord:", "user:", "pk:"]);
@@ -210,6 +208,7 @@ export async function preflightDiscordMessage(
const { code, created } = await upsertChannelPairingRequest({
channel: "discord",
id: author.id,
accountId: resolvedAccountId,
meta: {
tag: formatDiscordUserTag(author),
name: author.username ?? undefined,

View File

@@ -46,10 +46,7 @@ import { logVerbose } from "../../globals.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
import { buildPairingReply } from "../../pairing/pairing-messages.js";
import {
readChannelAllowFromStore,
upsertChannelPairingRequest,
} from "../../pairing/pairing-store.js";
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
import { buildUntrustedChannelMetadata } from "../../security/channel-metadata.js";
@@ -1363,8 +1360,8 @@ async function dispatchDiscordCommandInteraction(params: {
if (dmPolicy !== "open") {
const storeAllowFrom = await readStoreAllowFromForDmPolicy({
provider: "discord",
accountId,
dmPolicy,
readStore: (provider) => readChannelAllowFromStore(provider),
});
const effectiveAllowFrom = [
...(discordConfig?.allowFrom ?? discordConfig?.dm?.allowFrom ?? []),
@@ -1388,6 +1385,7 @@ async function dispatchDiscordCommandInteraction(params: {
const { code, created } = await upsertChannelPairingRequest({
channel: "discord",
id: user.id,
accountId,
meta: {
tag: sender.tag,
name: sender.name,