diff --git a/docs/channels/line.md b/docs/channels/line.md index 91e761790202..d5c371f1e366 100644 --- a/docs/channels/line.md +++ b/docs/channels/line.md @@ -140,8 +140,9 @@ Allowlists and policies: - `channels.line.dmPolicy`: `pairing | allowlist | open | disabled` (default `pairing`) - `channels.line.allowFrom`: allowlisted LINE user IDs for DMs; `dmPolicy: "open"` requires `["*"]` - `channels.line.groupPolicy`: `allowlist | open | disabled` (default `allowlist`) -- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups -- Per-group overrides: `channels.line.groups..allowFrom` (plus `enabled`, `requireMention`, `systemPrompt`, `skills`) +- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups; DM `allowFrom` entries do not admit group senders +- Per-group overrides: `channels.line.groups..allowFrom` (plus `enabled`, `requireMention`, `systemPrompt`, `skills`). With + `groupPolicy: "allowlist"`, set `groupAllowFrom` or the per-group `allowFrom`; an empty group allowlist blocks group messages even when DMs are open. - Static sender access groups can be referenced from `allowFrom`, `groupAllowFrom`, and per-group `allowFrom` with `accessGroup:`; see [Access groups](/channels/access-groups). - Runtime note: if `channels.line` is completely missing, runtime falls back to `groupPolicy="allowlist"` for group checks (even if `channels.defaults.groupPolicy` is set). diff --git a/extensions/line/src/bot-handlers.test.ts b/extensions/line/src/bot-handlers.test.ts index dc5b1285a4b7..eb5143f73b8e 100644 --- a/extensions/line/src/bot-handlers.test.ts +++ b/extensions/line/src/bot-handlers.test.ts @@ -524,6 +524,28 @@ describe("handleLineWebhookEvents", () => { expect(readAllowFromStoreMock).not.toHaveBeenCalled(); }); + it("does not use the DM allowlist when group allowlist policy has no group entries", async () => { + const processMessage = vi.fn(); + await expectGroupMessageBlocked({ + processMessage, + event: createReplayMessageEvent({ + messageId: "m5c", + groupId: "group-1", + userId: "user-open-dm", + webhookEventId: "evt-5c", + isRedelivery: false, + }), + context: createLineWebhookTestContext({ + processMessage, + dmPolicy: "open", + allowFrom: ["*"], + groupPolicy: "allowlist", + requireMention: false, + }), + }); + expect(readAllowFromStoreMock).not.toHaveBeenCalled(); + }); + it("blocks group messages without sender id when groupPolicy is allowlist", async () => { const processMessage = vi.fn(); const event = { diff --git a/extensions/line/src/bot-handlers.ts b/extensions/line/src/bot-handlers.ts index 5bc04442c800..9e7228fcc529 100644 --- a/extensions/line/src/bot-handlers.ts +++ b/extensions/line/src/bot-handlers.ts @@ -253,12 +253,10 @@ async function shouldProcessLineEvent( : groupConfig?.allowFrom !== undefined ? "allowlist" : runtimeGroupPolicy; + // LINE group allowlists are scoped separately from DM allowFrom. + // The shared ingress policy below intentionally keeps fallback disabled. const groupAllowFrom = normalizeStringEntries( - firstDefined( - groupConfig?.allowFrom, - account.config.groupAllowFrom, - account.config.allowFrom?.length ? account.config.allowFrom : undefined, - ), + firstDefined(groupConfig?.allowFrom, account.config.groupAllowFrom), ); const mentionFacts = (() => { if (!isGroup || event.type !== "message") {