diff --git a/packages/channels/weixin/src/api.test.ts b/packages/channels/weixin/src/api.test.ts new file mode 100644 index 00000000000..b850d7a38ad --- /dev/null +++ b/packages/channels/weixin/src/api.test.ts @@ -0,0 +1,51 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { uploadToCdn } from './api.js'; + +describe('uploadToCdn', () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it.each([ + 'HTTPS://novac2c.cdn.weixin.qq.com/c2c/upload?encrypted_query_param=abc', + 'HtTpS://novac2c.cdn.weixin.qq.com/c2c/upload?encrypted_query_param=abc', + ])('accepts %s CDN upload URLs', async (uploadUrl) => { + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + status: 200, + headers: { + get: vi.fn((name: string) => + name.toLowerCase() === 'x-encrypted-param' ? 'cdn-param' : null, + ), + }, + }); + vi.stubGlobal('fetch', fetchMock); + + await expect( + uploadToCdn(uploadUrl, 'filekey-123', Buffer.from('encrypted')), + ).resolves.toBe('cdn-param'); + + expect(fetchMock).toHaveBeenCalledWith( + uploadUrl, + expect.objectContaining({ + method: 'POST', + body: Buffer.from('encrypted'), + }), + ); + }); + + it('rejects uppercase HTTP CDN upload URLs', async () => { + const fetchMock = vi.fn(); + vi.stubGlobal('fetch', fetchMock); + + await expect( + uploadToCdn( + 'HTTP://novac2c.cdn.weixin.qq.com/c2c/upload?encrypted_query_param=abc', + 'filekey-123', + Buffer.from('encrypted'), + ), + ).rejects.toThrow('CDN upload URL must use HTTPS'); + + expect(fetchMock).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/channels/weixin/src/api.ts b/packages/channels/weixin/src/api.ts index 5097b978e5b..41ce2a23551 100644 --- a/packages/channels/weixin/src/api.ts +++ b/packages/channels/weixin/src/api.ts @@ -343,13 +343,14 @@ export async function uploadToCdn( const CDN_HOST = 'novac2c.cdn.weixin.qq.com'; let url: string; - if (urlOrParam.startsWith('https://')) { + const lowerUrlOrParam = urlOrParam.toLowerCase(); + if (lowerUrlOrParam.startsWith('https://')) { const parsed = new URL(urlOrParam); if (parsed.hostname !== CDN_HOST) { throw new Error(`CDN upload URL has unexpected host: ${parsed.hostname}`); } url = urlOrParam; - } else if (urlOrParam.startsWith('http://')) { + } else if (lowerUrlOrParam.startsWith('http://')) { throw new Error('CDN upload URL must use HTTPS'); } else { url = `https://${CDN_HOST}/c2c/upload?encrypted_query_param=${encodeURIComponent(urlOrParam)}&filekey=${encodeURIComponent(filekey)}`;