A4 收口企业微信入口与通知回 OA
这个提交包含在:
@@ -16,8 +16,8 @@ function buildDetailUrl(config, targetPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (path.startsWith('http://') || path.startsWith('https://')) {
|
||||
return path;
|
||||
if (/^[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith('//')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
||||
|
||||
@@ -114,6 +114,9 @@ describe('notificationDispatcher', () => {
|
||||
});
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessage.mock.calls[0][0].payload.detailUrl).toMatch(
|
||||
/^https:\/\/oa\.example\.com\/api\/auth\/deeplink\?token=/,
|
||||
);
|
||||
expect(deliveryState).toHaveLength(1);
|
||||
expect(deliveryState[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -137,6 +140,52 @@ describe('notificationDispatcher', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('does not pass external target URLs to WeCom messages', async () => {
|
||||
const { prisma, deliveryState } = createPrismaState({
|
||||
employees: [
|
||||
{ id: 'manager-1', status: 'active', notification_push_enabled: true, roles: ['manager'] },
|
||||
],
|
||||
channels: [buildChannel()],
|
||||
});
|
||||
const sendMessage = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
retryable: false,
|
||||
code: 'mock_sent',
|
||||
message: null,
|
||||
response: { mode: 'mock' },
|
||||
});
|
||||
|
||||
await dispatchNotificationBatch({
|
||||
prisma,
|
||||
config: {
|
||||
imNotifierEnabled: true,
|
||||
appOrigin: 'https://oa.example.com',
|
||||
jwtSecret: 'test-secret',
|
||||
},
|
||||
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
requestId: 'req-external-target',
|
||||
notifications: [
|
||||
{
|
||||
id: 'notification-1',
|
||||
recipient_employee_id: 'manager-1',
|
||||
target_type: 'workflow_task',
|
||||
},
|
||||
],
|
||||
type: 'workflow.task_assigned',
|
||||
title: '测试待办',
|
||||
body: '请处理审批',
|
||||
targetType: 'workflow_task',
|
||||
targetId: 'task-1',
|
||||
targetPath: 'https://evil.example.com/workflow/tasks/task-1',
|
||||
sendMessage,
|
||||
retryDelayMs: 0,
|
||||
});
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessage.mock.calls[0][0].payload.detailUrl).toBeNull();
|
||||
expect(deliveryState[0]).toEqual(expect.objectContaining({ status: 'success' }));
|
||||
});
|
||||
|
||||
it('retries retryable failures until a later success', async () => {
|
||||
const { prisma, deliveryState } = createPrismaState({
|
||||
employees: [
|
||||
|
||||
在新工单中引用
屏蔽一个用户