@dsh-overdrive/gateway 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/adapter.d.ts +7 -1
  2. package/dist/adapters/cli.d.ts +4 -1
  3. package/dist/adapters/cli.js +1 -1
  4. package/dist/adapters/cli.js.map +1 -1
  5. package/dist/adapters/dingtalk.d.ts +13 -5
  6. package/dist/adapters/dingtalk.js +27 -15
  7. package/dist/adapters/dingtalk.js.map +1 -1
  8. package/dist/adapters/discord.d.ts +4 -1
  9. package/dist/adapters/discord.js +4 -1
  10. package/dist/adapters/discord.js.map +1 -1
  11. package/dist/adapters/feishu.d.ts +4 -1
  12. package/dist/adapters/feishu.js +9 -3
  13. package/dist/adapters/feishu.js.map +1 -1
  14. package/dist/adapters/slack.d.ts +4 -1
  15. package/dist/adapters/slack.js +8 -3
  16. package/dist/adapters/slack.js.map +1 -1
  17. package/dist/adapters/telegram.d.ts +4 -1
  18. package/dist/adapters/telegram.js +5 -1
  19. package/dist/adapters/telegram.js.map +1 -1
  20. package/dist/adapters/wecom.d.ts +4 -1
  21. package/dist/adapters/wecom.js +1 -1
  22. package/dist/adapters/wecom.js.map +1 -1
  23. package/dist/adapters/whatsapp.d.ts +4 -1
  24. package/dist/adapters/whatsapp.js +8 -2
  25. package/dist/adapters/whatsapp.js.map +1 -1
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +13 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/session.d.ts +6 -2
  30. package/dist/session.js +8 -3
  31. package/dist/session.js.map +1 -1
  32. package/package.json +2 -2
  33. package/src/adapter.ts +8 -1
  34. package/src/adapters/cli.ts +3 -3
  35. package/src/adapters/dingtalk.ts +37 -17
  36. package/src/adapters/discord.ts +6 -3
  37. package/src/adapters/feishu.ts +16 -5
  38. package/src/adapters/slack.ts +14 -4
  39. package/src/adapters/telegram.ts +7 -3
  40. package/src/adapters/wecom.ts +3 -3
  41. package/src/adapters/whatsapp.ts +10 -4
  42. package/src/index.ts +15 -3
  43. package/src/session.ts +9 -3
  44. package/test/adapters.dingtalk.test.ts +11 -5
  45. package/test/multi.test.ts +29 -8
  46. package/test/session.test.ts +7 -2
  47. package/test/streaming.test.ts +162 -162
package/dist/index.js CHANGED
@@ -108,7 +108,7 @@ async function handleCommand(adapter, client, command, sessionId, chatId, aggreg
108
108
  }
109
109
  /** 单个适配器的接线:白名单 → 命令面/upsert → sendMessage;按钮 → resolveApproval;事件流经轨迹聚合。 */
110
110
  export async function wireAdapter(adapter, client, opts) {
111
- const allow = new Allowlist(opts.allowlist);
111
+ const allow = new Allowlist(opts.allowlist, opts.allowAll);
112
112
  const chatIds = new Map();
113
113
  const aggregator = new TrajectoryAggregator();
114
114
  const deltas = new DeltaTracker();
@@ -147,7 +147,7 @@ export async function wireAdapter(adapter, client, opts) {
147
147
  await adapter.send(msg.chatId, { text: `❌ 出错了:${message}` }).catch(() => undefined);
148
148
  }
149
149
  });
150
- adapter.onReply(async (buttonId) => {
150
+ adapter.onReply(async (buttonId, sender) => {
151
151
  try {
152
152
  const idx = buttonId.indexOf(':');
153
153
  if (idx < 0)
@@ -155,6 +155,15 @@ export async function wireAdapter(adapter, client, opts) {
155
155
  const action = buttonId.slice(0, idx);
156
156
  const reqId = buttonId.slice(idx + 1);
157
157
  if ((action === 'approve' || action === 'reject') && reqId) {
158
+ // 安全边界:审批按钮必须校验点击者(维护者评审指出:未授权用户可代授权)
159
+ const senderKey = buildSessionKey(adapter.id, { chatId: sender.chatId, userId: sender.userId });
160
+ if (!allow.allows(senderKey)) {
161
+ console.warn(`[gateway][${adapter.id}] 按钮点击者不在白名单,拒绝批准: ${senderKey}`);
162
+ if (sender.chatId) {
163
+ await adapter.send(sender.chatId, { text: '⛔ 你不在白名单里,不能批准该操作。' }).catch(() => undefined);
164
+ }
165
+ return;
166
+ }
158
167
  await client.resolveApproval(reqId, action);
159
168
  }
160
169
  }
@@ -192,6 +201,7 @@ async function main() {
192
201
  const dshToken = process.env.DSH_TOKEN ?? 'dev-token';
193
202
  const allowlist = (process.env.ALLOWLIST ?? '')
194
203
  .split(',').map((s) => s.trim()).filter(Boolean);
204
+ const allowAll = process.env.ALLOW_ALL === '1';
195
205
  const adapterIds = parseAdapterIds(process.env.GATEWAY_ADAPTERS ?? 'cli');
196
206
  const env = adapterEnvFromProcess();
197
207
  const asr = createTranscriber({
@@ -206,7 +216,7 @@ async function main() {
206
216
  const adapters = adapterIds.map((id) => createAdapter(id, env));
207
217
  for (const adapter of adapters) {
208
218
  await adapter.connect();
209
- await wireAdapter(adapter, client, { allowlist, asr });
219
+ await wireAdapter(adapter, client, { allowlist, allowAll, asr });
210
220
  console.log(`[gateway] ${adapter.id} 适配器已就绪`);
211
221
  }
212
222
  const consolePort = Number(process.env.GATEWAY_CONSOLE_PORT ?? 3190);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/session.d.ts CHANGED
@@ -2,9 +2,13 @@ export declare function buildSessionKey(adapterId: string, msg: {
2
2
  chatId: string;
3
3
  userId: string;
4
4
  }): string;
5
- /** 空列表 = 开发模式放行所有;生产环境必须显式配置。 */
5
+ /**
6
+ * 白名单:默认 fail-closed —— 只有显式配置了条目才放行;
7
+ * 开发环境可用 ALLOW_ALL=1 显式放行所有(比空列表隐式放行安全得多)。
8
+ */
6
9
  export declare class Allowlist {
7
10
  private readonly entries;
8
- constructor(entries: string[]);
11
+ private readonly allowAll;
12
+ constructor(entries: string[], allowAll?: boolean);
9
13
  allows(key: string): boolean;
10
14
  }
package/dist/session.js CHANGED
@@ -2,14 +2,19 @@ import { sessionKey } from '@dsh-overdrive/sdk';
2
2
  export function buildSessionKey(adapterId, msg) {
3
3
  return sessionKey(adapterId, msg.chatId, msg.userId);
4
4
  }
5
- /** 空列表 = 开发模式放行所有;生产环境必须显式配置。 */
5
+ /**
6
+ * 白名单:默认 fail-closed —— 只有显式配置了条目才放行;
7
+ * 开发环境可用 ALLOW_ALL=1 显式放行所有(比空列表隐式放行安全得多)。
8
+ */
6
9
  export class Allowlist {
7
10
  entries;
8
- constructor(entries) {
11
+ allowAll;
12
+ constructor(entries, allowAll = false) {
9
13
  this.entries = entries;
14
+ this.allowAll = allowAll;
10
15
  }
11
16
  allows(key) {
12
- return this.entries.length === 0 || this.entries.includes(key);
17
+ return this.allowAll || (this.entries.length > 0 && this.entries.includes(key));
13
18
  }
14
19
  }
15
20
  //# sourceMappingURL=session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,GAAuC;IAEvC,OAAO,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,iCAAiC;AACjC,MAAM,OAAO,SAAS;IACS;IAA7B,YAA6B,OAAiB;QAAjB,YAAO,GAAP,OAAO,CAAU;IAAG,CAAC;IAElD,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACjE,CAAC;CACF"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,GAAuC;IAEvC,OAAO,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,SAAS;IAED;IACA;IAFnB,YACmB,OAAiB,EACjB,WAAW,KAAK;QADhB,YAAO,GAAP,OAAO,CAAU;QACjB,aAAQ,GAAR,QAAQ,CAAQ;IAChC,CAAC;IAEJ,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
- {
1
+ {
2
2
  "name": "@dsh-overdrive/gateway",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "tsc"
package/src/adapter.ts CHANGED
@@ -12,6 +12,12 @@ export interface OutboundPayload {
12
12
  buttons?: OutboundButton[];
13
13
  }
14
14
 
15
+ /** 按钮回执的点击者身份(用于白名单校验)。chatId 在个别平台回调中可能缺失,缺失时按未授权处理(fail-closed)。 */
16
+ export interface ReplySender {
17
+ chatId: string;
18
+ userId: string;
19
+ }
20
+
15
21
  /** 平台适配器契约:M2/M3 的 WhatsApp/Telegram/… 都实现它。 */
16
22
  export interface Adapter {
17
23
  readonly id: string;
@@ -22,5 +28,6 @@ export interface Adapter {
22
28
  /** 可选:连接状态(供控制台)。 */
23
29
  status?(): { connected: boolean };
24
30
  onMessage(cb: (msg: NormalizedMessage) => void): void;
25
- onReply(cb: (buttonId: string) => void): void;
31
+ /** 按钮点击回执:buttonId + 点击者身份。身份缺失即传空字符串,由上层按未授权处理。 */
32
+ onReply(cb: (buttonId: string, sender: ReplySender) => void): void;
26
33
  }
@@ -5,7 +5,7 @@ import type { Adapter, NormalizedMessage, OutboundPayload } from '../adapter.js'
5
5
  export class CliAdapter implements Adapter {
6
6
  readonly id = 'cli';
7
7
  private messageCb?: (msg: NormalizedMessage) => void;
8
- private replyCb?: (buttonId: string) => void;
8
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
9
9
  private rl?: ReturnType<typeof createInterface>;
10
10
 
11
11
  async connect(): Promise<void> {
@@ -15,7 +15,7 @@ export class CliAdapter implements Adapter {
15
15
  if (!trimmed) return;
16
16
  const btn = trimmed.match(/^\/btn\s+(\S+)$/i);
17
17
  if (btn) {
18
- this.replyCb?.(btn[1]);
18
+ this.replyCb?.(btn[1], { chatId: 'cli', userId: 'local' });
19
19
  return;
20
20
  }
21
21
  this.messageCb?.({ chatId: 'cli', userId: 'local', text: trimmed });
@@ -31,7 +31,7 @@ export class CliAdapter implements Adapter {
31
31
  }
32
32
 
33
33
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
34
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
34
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
35
35
  /** CLI 是本地进程内适配器:恒为已连接。 */
36
36
  status(): { connected: boolean } { return { connected: true }; }
37
37
  }
@@ -70,31 +70,46 @@ export function buildActionCard(text: string, buttons: OutboundButton[]): {
70
70
  };
71
71
  }
72
72
 
73
+ export interface CardCallbackResult {
74
+ buttonId: string;
75
+ chatId?: string;
76
+ userId?: string;
77
+ }
78
+
73
79
  /**
74
- * 钉钉卡片回调载荷 → 按钮 id("approve:<reqId>")。
75
- * Stream 模式下回调 JSON 的字段名(cardCallbackData / params / cardActionData)在不同卡片版本有差异,
76
- * 这里做多路径深度兜底解析;真机验证后可按实际字段收敛。找不到返回 null。
80
+ * 钉钉卡片回调载荷 → { buttonId, chatId?, userId? }。
81
+ * Stream 模式下回调 JSON 的字段名(cardCallbackData / params / cardActionData,以及会话/用户字段)
82
+ * 在不同卡片版本有差异,这里做多路径深度兜底解析;真机验证后可按实际字段收敛。找不到返回 null。
77
83
  */
78
- export function parseCardCallback(raw: unknown): string | null {
79
- const visit = (obj: unknown, depth: number): string | null => {
80
- if (depth > 5 || !obj || typeof obj !== 'object') return null;
84
+ export function parseCardCallback(raw: unknown): CardCallbackResult | null {
85
+ let buttonId: string | null = null;
86
+ let chatId: string | undefined;
87
+ let userId: string | undefined;
88
+
89
+ const visit = (obj: unknown, depth: number): void => {
90
+ if (depth > 5 || !obj || typeof obj !== 'object') return;
81
91
  for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
82
- if (typeof value === 'string' && (key === 'cardCallbackData' || key === 'params' || key === 'cardActionData')) {
92
+ if (!buttonId && typeof value === 'string' && (key === 'cardCallbackData' || key === 'params' || key === 'cardActionData')) {
83
93
  try {
84
94
  const parsed = JSON.parse(value) as { action?: string; reqId?: string };
85
95
  if ((parsed.action === 'approve' || parsed.action === 'reject') && typeof parsed.reqId === 'string' && parsed.reqId) {
86
- return `${parsed.action}:${parsed.reqId}`;
96
+ buttonId = `${parsed.action}:${parsed.reqId}`;
87
97
  }
88
98
  } catch {
89
99
  /* 该字段不是 JSON 载荷,继续往下找 */
90
100
  }
91
101
  }
92
- const found = visit(value, depth + 1);
93
- if (found) return found;
102
+ if (!chatId && typeof value === 'string' && (key === 'conversationId' || key === 'conversation_id') && value) {
103
+ chatId = value;
104
+ }
105
+ if (!userId && typeof value === 'string' && (key === 'senderStaffId' || key === 'senderId' || key === 'userid' || key === 'userId') && value) {
106
+ userId = value;
107
+ }
108
+ if (typeof value === 'object') visit(value, depth + 1);
94
109
  }
95
- return null;
96
110
  };
97
- return visit(raw, 0);
111
+ visit(raw, 0);
112
+ return buttonId ? { buttonId, chatId, userId } : null;
98
113
  }
99
114
 
100
115
  // ── 适配器 ────────────────────────────────────────────────────
@@ -109,7 +124,7 @@ export class DingTalkAdapter implements Adapter {
109
124
  private client?: DWClient;
110
125
  private connected = false;
111
126
  private messageCb?: (msg: NormalizedMessage) => void;
112
- private replyCb?: (buttonId: string) => void;
127
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
113
128
  private readonly pendingButtons = new PendingButtons();
114
129
  /** conversationId → 最近的 sessionWebhook(回复通道,过期由钉钉侧管理) */
115
130
  private readonly webhooks = new Map<string, string>();
@@ -131,7 +146,7 @@ export class DingTalkAdapter implements Adapter {
131
146
  this.webhooks.set(parsed.chatId, parsed.sessionWebhook);
132
147
  const button = this.pendingButtons.match(parsed.chatId, parsed.text);
133
148
  if (button) {
134
- this.replyCb?.(button.id);
149
+ this.replyCb?.(button.id, { chatId: parsed.chatId, userId: parsed.userId });
135
150
  return;
136
151
  }
137
152
  this.messageCb?.({ chatId: parsed.chatId, userId: parsed.userId, text: parsed.text });
@@ -144,8 +159,13 @@ export class DingTalkAdapter implements Adapter {
144
159
  } catch {
145
160
  return;
146
161
  }
147
- const buttonId = parseCardCallback(data);
148
- if (buttonId) this.replyCb?.(buttonId);
162
+ const result = parseCardCallback(data);
163
+ if (result) {
164
+ this.replyCb?.(result.buttonId, {
165
+ chatId: result.chatId ?? result.userId ?? '',
166
+ userId: result.userId ?? '',
167
+ });
168
+ }
149
169
  });
150
170
  await client.connect();
151
171
  this.connected = true;
@@ -181,6 +201,6 @@ export class DingTalkAdapter implements Adapter {
181
201
  }
182
202
 
183
203
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
184
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
204
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
185
205
  status(): { connected: boolean } { return { connected: this.connected }; }
186
206
  }
@@ -64,7 +64,7 @@ export class DiscordAdapter implements Adapter {
64
64
  private readonly client: Client;
65
65
  private connected = false;
66
66
  private messageCb?: (msg: NormalizedMessage) => void;
67
- private replyCb?: (buttonId: string) => void;
67
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
68
68
 
69
69
  constructor(opts: DiscordAdapterOptions) {
70
70
  this.client = new Client({
@@ -89,7 +89,10 @@ export class DiscordAdapter implements Adapter {
89
89
  if (!interaction.isButton()) return;
90
90
  const button = interaction as ButtonInteraction;
91
91
  await button.deferUpdate().catch(() => undefined);
92
- this.replyCb?.(button.customId);
92
+ this.replyCb?.(button.customId, {
93
+ chatId: button.channelId,
94
+ userId: button.user.id,
95
+ });
93
96
  });
94
97
  }
95
98
 
@@ -112,6 +115,6 @@ export class DiscordAdapter implements Adapter {
112
115
  }
113
116
 
114
117
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
115
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
118
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
116
119
  status(): { connected: boolean } { return { connected: this.connected }; }
117
120
  }
@@ -96,7 +96,7 @@ export class FeishuAdapter implements Adapter {
96
96
  private ws?: InstanceType<typeof WSClient>;
97
97
  private connected = false;
98
98
  private messageCb?: (msg: NormalizedMessage) => void;
99
- private replyCb?: (buttonId: string) => void;
99
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
100
100
  private readonly pendingButtons = new PendingButtons();
101
101
  /** chatId → 最近一条入站消息的 message_id(send 优先 reply,缺失则 create 兜底) */
102
102
  private readonly lastMessageIds = new Map<string, string>();
@@ -117,15 +117,26 @@ export class FeishuAdapter implements Adapter {
117
117
  const chatId = normalized.chatId;
118
118
  const button = this.pendingButtons.match(chatId, normalized.text);
119
119
  if (button) {
120
- this.replyCb?.(button.id);
120
+ this.replyCb?.(button.id, { chatId, userId: normalized.userId });
121
121
  return;
122
122
  }
123
123
  this.messageCb?.(normalized);
124
124
  },
125
125
  // 原生交互卡片按钮回调 → 审批应答(Roadmap v0.2)
126
- 'card.action.trigger': async (data: { action?: { value?: unknown } }) => {
126
+ // 载荷字段(operator.open_id / context.open_chat_id)取自官方卡片回调事件;
127
+ // 个别版本字段名可能不同 —— 拿不到身份时上层按未授权处理(fail-closed),编号回复兜底不受影响。
128
+ 'card.action.trigger': async (data: {
129
+ action?: { value?: unknown };
130
+ operator?: { open_id?: string };
131
+ context?: { open_chat_id?: string };
132
+ }) => {
127
133
  const buttonId = cardActionToButtonId(data?.action?.value);
128
- if (buttonId) this.replyCb?.(buttonId);
134
+ if (buttonId) {
135
+ this.replyCb?.(buttonId, {
136
+ chatId: data?.context?.open_chat_id ?? '',
137
+ userId: data?.operator?.open_id ?? '',
138
+ });
139
+ }
129
140
  },
130
141
  });
131
142
  this.ws = new WSClient({
@@ -175,6 +186,6 @@ export class FeishuAdapter implements Adapter {
175
186
  }
176
187
 
177
188
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
178
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
189
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
179
190
  status(): { connected: boolean } { return { connected: this.connected }; }
180
191
  }
@@ -69,7 +69,7 @@ export class SlackAdapter implements Adapter {
69
69
  private readonly app: InstanceType<typeof App>;
70
70
  private connected = false;
71
71
  private messageCb?: (msg: NormalizedMessage) => void;
72
- private replyCb?: (buttonId: string) => void;
72
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
73
73
 
74
74
  constructor(opts: SlackAdapterOptions) {
75
75
  this.app = new App({ token: opts.botToken, appToken: opts.appToken, socketMode: true });
@@ -82,8 +82,18 @@ export class SlackAdapter implements Adapter {
82
82
  });
83
83
  this.app.action(/^approve:|^reject:/, async ({ ack, body, respond }) => {
84
84
  await ack();
85
- const action = (body as { actions?: Array<{ value?: string }> }).actions?.[0];
86
- if (action?.value) this.replyCb?.(action.value);
85
+ const b = body as {
86
+ actions?: Array<{ value?: string }>;
87
+ user?: { id?: string };
88
+ channel?: { id?: string };
89
+ };
90
+ const action = b.actions?.[0];
91
+ if (action?.value) {
92
+ this.replyCb?.(action.value, {
93
+ chatId: b.channel?.id ?? '',
94
+ userId: b.user?.id ?? '',
95
+ });
96
+ }
87
97
  await respond({ text: '处理中…', replace_original: false }).catch(() => undefined);
88
98
  });
89
99
  await this.app.start(0); // Socket Mode 不需要端口;start(0) 仅建立连接
@@ -100,6 +110,6 @@ export class SlackAdapter implements Adapter {
100
110
  }
101
111
 
102
112
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
103
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
113
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
104
114
  status(): { connected: boolean } { return { connected: this.connected }; }
105
115
  }
@@ -69,7 +69,7 @@ export class TelegramAdapter implements Adapter {
69
69
  private readonly token: string;
70
70
  private connected = false;
71
71
  private messageCb?: (msg: NormalizedMessage) => void;
72
- private replyCb?: (buttonId: string) => void;
72
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
73
73
 
74
74
  constructor(opts: TelegramAdapterOptions) {
75
75
  this.token = opts.token;
@@ -86,7 +86,11 @@ export class TelegramAdapter implements Adapter {
86
86
  this.bot.on('callback_query:data', async (ctx) => {
87
87
  const data = ctx.callbackQuery.data;
88
88
  await ctx.answerCallbackQuery().catch(() => undefined);
89
- this.replyCb?.(data);
89
+ const chat = ctx.callbackQuery.message?.chat as { id?: number | string } | undefined;
90
+ this.replyCb?.(data, {
91
+ chatId: String(chat?.id ?? ''),
92
+ userId: String(ctx.callbackQuery.from.id),
93
+ });
90
94
  });
91
95
  this.bot.catch((err) => console.error('[telegram]', err));
92
96
  void this.bot.start(); // 长轮询(自托管无需 webhook)
@@ -124,6 +128,6 @@ export class TelegramAdapter implements Adapter {
124
128
  }
125
129
 
126
130
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
127
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
131
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
128
132
  status(): { connected: boolean } { return { connected: this.connected }; }
129
133
  }
@@ -98,7 +98,7 @@ export class WeComAdapter implements Adapter {
98
98
  private server?: ReturnType<typeof createServer>;
99
99
  private connected = false;
100
100
  private messageCb?: (msg: NormalizedMessage) => void;
101
- private replyCb?: (buttonId: string) => void;
101
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
102
102
  private readonly pendingButtons = new PendingButtons();
103
103
  /** access_token 缓存:企业微信 token 有效期 7200s,且有获取频率限制,必须复用。 */
104
104
  private tokenCache?: { token: string; expiresAt: number };
@@ -143,7 +143,7 @@ export class WeComAdapter implements Adapter {
143
143
  if (!normalized) return;
144
144
  const button = this.pendingButtons.match(normalized.chatId, normalized.text);
145
145
  if (button) {
146
- this.replyCb?.(button.id);
146
+ this.replyCb?.(button.id, { chatId: normalized.chatId, userId: normalized.userId });
147
147
  return;
148
148
  }
149
149
  this.messageCb?.(normalized);
@@ -178,7 +178,7 @@ export class WeComAdapter implements Adapter {
178
178
  }
179
179
 
180
180
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
181
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
181
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
182
182
  status(): { connected: boolean } { return { connected: this.connected }; }
183
183
  }
184
184
 
@@ -129,7 +129,7 @@ export class WhatsAppAdapter implements Adapter {
129
129
  private sock?: WASocket;
130
130
  private connected = false;
131
131
  private messageCb?: (msg: NormalizedMessage) => void;
132
- private replyCb?: (buttonId: string) => void;
132
+ private replyCb?: (buttonId: string, sender: { chatId: string; userId: string }) => void;
133
133
  /** chatId → 当前 pending 按钮(编号回复 → 按钮 id,带 TTL 防过期误吞) */
134
134
  private readonly pendingButtons = new PendingButtons();
135
135
 
@@ -175,7 +175,10 @@ export class WhatsAppAdapter implements Adapter {
175
175
  if (buttonId) {
176
176
  const chatId = waRaw.key?.remoteJid;
177
177
  if (chatId) this.pendingButtons.consume(chatId);
178
- this.replyCb?.(buttonId);
178
+ this.replyCb?.(buttonId, {
179
+ chatId: chatId ?? '',
180
+ userId: waRaw.key?.participant ?? chatId ?? '',
181
+ });
179
182
  continue;
180
183
  }
181
184
  const normalized = normalizeWhatsAppMessage(waRaw);
@@ -183,7 +186,10 @@ export class WhatsAppAdapter implements Adapter {
183
186
  // 编号回复兜底:若该 chat 有 pending 按钮且消息是数字(TTL 内),转成按钮点击
184
187
  const button = this.pendingButtons.match(normalized.msg.chatId, normalized.msg.text);
185
188
  if (button) {
186
- this.replyCb?.(button.id);
189
+ this.replyCb?.(button.id, {
190
+ chatId: normalized.msg.chatId,
191
+ userId: normalized.msg.userId,
192
+ });
187
193
  continue;
188
194
  }
189
195
  this.messageCb?.(normalized.msg);
@@ -223,6 +229,6 @@ export class WhatsAppAdapter implements Adapter {
223
229
  }
224
230
 
225
231
  onMessage(cb: (msg: NormalizedMessage) => void): void { this.messageCb = cb; }
226
- onReply(cb: (buttonId: string) => void): void { this.replyCb = cb; }
232
+ onReply(cb: (buttonId: string, sender: { chatId: string; userId: string }) => void): void { this.replyCb = cb; }
227
233
  status(): { connected: boolean } { return { connected: this.connected }; }
228
234
  }
package/src/index.ts CHANGED
@@ -65,6 +65,8 @@ export function planOutbound(ev: ServerEvent): { payload: OutboundPayload } | nu
65
65
 
66
66
  export interface WireOptions {
67
67
  allowlist: string[];
68
+ /** 开发逃生口:ALLOW_ALL=1 跳过白名单(生产勿开)。 */
69
+ allowAll?: boolean;
68
70
  /** ASR 转写器;配置了 API key 时启用,语音消息转成文本再发给 agent。 */
69
71
  asr?: AsrTranscriber;
70
72
  }
@@ -131,7 +133,7 @@ export async function wireAdapter(
131
133
  client: GatewayClient,
132
134
  opts: WireOptions,
133
135
  ): Promise<void> {
134
- const allow = new Allowlist(opts.allowlist);
136
+ const allow = new Allowlist(opts.allowlist, opts.allowAll);
135
137
  const chatIds = new Map<string, string>();
136
138
  const aggregator = new TrajectoryAggregator();
137
139
  const deltas = new DeltaTracker();
@@ -174,13 +176,22 @@ export async function wireAdapter(
174
176
  }
175
177
  });
176
178
 
177
- adapter.onReply(async (buttonId) => {
179
+ adapter.onReply(async (buttonId, sender) => {
178
180
  try {
179
181
  const idx = buttonId.indexOf(':');
180
182
  if (idx < 0) return;
181
183
  const action = buttonId.slice(0, idx) as 'approve' | 'reject';
182
184
  const reqId = buttonId.slice(idx + 1);
183
185
  if ((action === 'approve' || action === 'reject') && reqId) {
186
+ // 安全边界:审批按钮必须校验点击者(维护者评审指出:未授权用户可代授权)
187
+ const senderKey = buildSessionKey(adapter.id, { chatId: sender.chatId, userId: sender.userId });
188
+ if (!allow.allows(senderKey)) {
189
+ console.warn(`[gateway][${adapter.id}] 按钮点击者不在白名单,拒绝批准: ${senderKey}`);
190
+ if (sender.chatId) {
191
+ await adapter.send(sender.chatId, { text: '⛔ 你不在白名单里,不能批准该操作。' }).catch(() => undefined);
192
+ }
193
+ return;
194
+ }
184
195
  await client.resolveApproval(reqId, action);
185
196
  }
186
197
  } catch (error) {
@@ -221,6 +232,7 @@ async function main(): Promise<void> {
221
232
  const dshToken = process.env.DSH_TOKEN ?? 'dev-token';
222
233
  const allowlist = (process.env.ALLOWLIST ?? '')
223
234
  .split(',').map((s) => s.trim()).filter(Boolean);
235
+ const allowAll = process.env.ALLOW_ALL === '1';
224
236
  const adapterIds = parseAdapterIds(process.env.GATEWAY_ADAPTERS ?? 'cli');
225
237
  const env = adapterEnvFromProcess();
226
238
  const asr = createTranscriber({
@@ -236,7 +248,7 @@ async function main(): Promise<void> {
236
248
  const adapters: Adapter[] = adapterIds.map((id) => createAdapter(id, env));
237
249
  for (const adapter of adapters) {
238
250
  await adapter.connect();
239
- await wireAdapter(adapter, client, { allowlist, asr });
251
+ await wireAdapter(adapter, client, { allowlist, allowAll, asr });
240
252
  console.log(`[gateway] ${adapter.id} 适配器已就绪`);
241
253
  }
242
254
 
package/src/session.ts CHANGED
@@ -7,11 +7,17 @@ export function buildSessionKey(
7
7
  return sessionKey(adapterId, msg.chatId, msg.userId);
8
8
  }
9
9
 
10
- /** 空列表 = 开发模式放行所有;生产环境必须显式配置。 */
10
+ /**
11
+ * 白名单:默认 fail-closed —— 只有显式配置了条目才放行;
12
+ * 开发环境可用 ALLOW_ALL=1 显式放行所有(比空列表隐式放行安全得多)。
13
+ */
11
14
  export class Allowlist {
12
- constructor(private readonly entries: string[]) {}
15
+ constructor(
16
+ private readonly entries: string[],
17
+ private readonly allowAll = false,
18
+ ) {}
13
19
 
14
20
  allows(key: string): boolean {
15
- return this.entries.length === 0 || this.entries.includes(key);
21
+ return this.allowAll || (this.entries.length > 0 && this.entries.includes(key));
16
22
  }
17
23
  }
@@ -37,13 +37,19 @@ describe('buildActionCard(钉钉 actionCard)', () => {
37
37
  });
38
38
  });
39
39
 
40
- describe('parseCardCallback(TOPIC_CARD 回调载荷 → 按钮 id', () => {
40
+ describe('parseCardCallback(TOPIC_CARD 回调载荷 → 按钮 id + 身份)', () => {
41
41
  it('识别 cardCallbackData 字段', () => {
42
- expect(parseCardCallback({ cardPrivateData: { cardCallbackData: '{"action":"approve","reqId":"r1"}' } })).toBe('approve:r1');
42
+ expect(parseCardCallback({ cardPrivateData: { cardCallbackData: '{"action":"approve","reqId":"r1"}' } })).toMatchObject({ buttonId: 'approve:r1' });
43
43
  });
44
44
  it('识别 params / cardActionData 字段与嵌套结构', () => {
45
- expect(parseCardCallback({ cardPrivateData: { params: '{"action":"reject","reqId":"r9"}' } })).toBe('reject:r9');
46
- expect(parseCardCallback({ a: { b: { cardActionData: '{"action":"approve","reqId":"x"}' } } })).toBe('approve:x');
45
+ expect(parseCardCallback({ cardPrivateData: { params: '{"action":"reject","reqId":"r9"}' } })).toMatchObject({ buttonId: 'reject:r9' });
46
+ expect(parseCardCallback({ a: { b: { cardActionData: '{"action":"approve","reqId":"x"}' } } })).toMatchObject({ buttonId: 'approve:x' });
47
+ });
48
+ it('带回会话与用户身份(用于白名单校验)', () => {
49
+ expect(parseCardCallback({
50
+ cardPrivateData: { cardCallbackData: '{"action":"approve","reqId":"r1"}', userId: 'u1' },
51
+ conversationId: 'cid1',
52
+ })).toEqual({ buttonId: 'approve:r1', chatId: 'cid1', userId: 'u1' });
47
53
  });
48
54
  it('非法载荷返回 null', () => {
49
55
  expect(parseCardCallback(null)).toBeNull();
@@ -53,6 +59,6 @@ describe('parseCardCallback(TOPIC_CARD 回调载荷 → 按钮 id)', () => {
53
59
  });
54
60
  it('buttonCallbackData 与 parseCardCallback 往返一致', () => {
55
61
  const data = buttonCallbackData({ id: 'reject:r7', label: 'x' });
56
- expect(parseCardCallback({ cardPrivateData: { cardCallbackData: data } })).toBe('reject:r7');
62
+ expect(parseCardCallback({ cardPrivateData: { cardCallbackData: data } })).toMatchObject({ buttonId: 'reject:r7' });
57
63
  });
58
64
  });