fix(line): require wildcard for open dm policy

This commit is contained in:
Peter Steinberger
2026-05-06 07:35:10 +01:00
parent 24fc6a435f
commit 5e05052bb9
4 changed files with 99 additions and 7 deletions

View File

@@ -108,6 +108,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Feishu: hydrate missing native topic starter thread IDs before session routing so first turns and follow-ups stay in the same topic session. Fixes #78262. Thanks @joeyzenghuan.
- LINE: reject `dmPolicy: "open"` configs without wildcard `allowFrom` so webhook DMs fail validation instead of being acknowledged and silently blocked before inbound processing. Fixes #78316.
- Providers/xAI: stop sending OpenAI-style reasoning effort controls to native Grok Responses models, so `xai/grok-4.3` no longer fails live Docker/Gateway runs with `Invalid reasoning effort`.
- Providers/xAI: clamp the bundled xAI thinking profile to `off` so live Gateway runs cannot send unsupported reasoning levels to native Grok Responses models.
- Matrix/approvals: retry approval delivery up to 3 times with a short backoff so transient Matrix send failures do not strand pending approval prompts. (#78179) Thanks @Patrick-Erichsen.

View File

@@ -68,6 +68,22 @@ Minimal config:
}
```
Public DM config:
```json5
{
channels: {
line: {
enabled: true,
channelAccessToken: "LINE_CHANNEL_ACCESS_TOKEN",
channelSecret: "LINE_CHANNEL_SECRET",
dmPolicy: "open",
allowFrom: ["*"],
},
},
}
```
Env vars (default account only):
- `LINE_CHANNEL_ACCESS_TOKEN`
@@ -119,7 +135,7 @@ openclaw pairing approve line <CODE>
Allowlists and policies:
- `channels.line.dmPolicy`: `pairing | allowlist | open | disabled`
- `channels.line.allowFrom`: allowlisted LINE user IDs for DMs
- `channels.line.allowFrom`: allowlisted LINE user IDs for DMs; `dmPolicy: "open"` requires `["*"]`
- `channels.line.groupPolicy`: `allowlist | open | disabled`
- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups
- Per-group overrides: `channels.line.groups.<groupId>.allowFrom`

View File

@@ -0,0 +1,51 @@
import { describe, expect, it } from "vitest";
import { LineConfigSchema } from "./config-schema.js";
describe("LineConfigSchema", () => {
it('rejects dmPolicy="open" without wildcard allowFrom', () => {
const result = LineConfigSchema.safeParse({
channelAccessToken: "token",
channelSecret: "secret",
dmPolicy: "open",
});
expect(result.success).toBe(false);
expect(result.error.issues).toEqual([
expect.objectContaining({
path: ["allowFrom"],
message: 'channels.line.dmPolicy="open" requires channels.line.allowFrom to include "*"',
}),
]);
});
it('accepts dmPolicy="open" with wildcard allowFrom', () => {
const result = LineConfigSchema.safeParse({
channelAccessToken: "token",
channelSecret: "secret",
dmPolicy: "open",
allowFrom: ["*"],
});
expect(result.success).toBe(true);
});
it('rejects account dmPolicy="open" without wildcard allowFrom', () => {
const result = LineConfigSchema.safeParse({
accounts: {
work: {
channelAccessToken: "token",
channelSecret: "secret",
dmPolicy: "open",
},
},
});
expect(result.success).toBe(false);
expect(result.error.issues).toEqual([
expect.objectContaining({
path: ["accounts", "work", "allowFrom"],
message: 'channels.line.dmPolicy="open" requires channels.line.allowFrom to include "*"',
}),
]);
});
});

View File

@@ -1,4 +1,8 @@
import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
import {
buildChannelConfigSchema,
requireOpenAllowFrom,
} from "openclaw/plugin-sdk/channel-config-schema";
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
import { z } from "openclaw/plugin-sdk/zod";
const DmPolicySchema = z.enum(["open", "allowlist", "pairing", "disabled"]);
@@ -15,7 +19,7 @@ const ThreadBindingsSchema = z
})
.strict();
const LineCommonConfigSchema = z.object({
const LineCommonConfigSchemaBase = z.object({
enabled: z.boolean().optional(),
channelAccessToken: z.string().optional(),
channelSecret: z.string().optional(),
@@ -42,15 +46,35 @@ const LineGroupConfigSchema = z
})
.strict();
const LineAccountConfigSchema = LineCommonConfigSchema.extend({
const LineAccountConfigSchema = LineCommonConfigSchemaBase.extend({
groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional(),
}).strict();
})
.strict()
.superRefine((value, ctx) => {
requireChannelOpenAllowFrom({
channel: "line",
policy: value.dmPolicy,
allowFrom: value.allowFrom,
ctx,
requireOpenAllowFrom,
});
});
export const LineConfigSchema = LineCommonConfigSchema.extend({
export const LineConfigSchema = LineCommonConfigSchemaBase.extend({
accounts: z.record(z.string(), LineAccountConfigSchema.optional()).optional(),
defaultAccount: z.string().optional(),
groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional(),
}).strict();
})
.strict()
.superRefine((value, ctx) => {
requireChannelOpenAllowFrom({
channel: "line",
policy: value.dmPolicy,
allowFrom: value.allowFrom,
ctx,
requireOpenAllowFrom,
});
});
export const LineChannelConfigSchema = buildChannelConfigSchema(LineConfigSchema);