import { createHmac } from 'node:crypto' import { describe, expect, it } from 'vitest' import { isPublicAddress, signWebhook } from '../src/webhooks.js' describe('Webhook 签名', () => { it('sha256=HMAC(secret, timestamp + "." + body),接收方用同一 secret 可验证', () => { const body = '{"id":"42","type":"task.accepted"}' const signature = signWebhook('s3cret', '1791600000', body) expect(signature).toBe(`sha256=${createHmac('sha256', 's3cret').update(`1791600000.${body}`).digest('hex')}`) expect(signature).toMatch(/^sha256=[0-9a-f]{64}$/) expect(signWebhook('s3cret', '1791600001', body)).not.toBe(signature) expect(signWebhook('other', '1791600000', body)).not.toBe(signature) }) }) describe('Webhook 目标地址(防 SSRF)', () => { it.each(['127.0.0.1', '10.1.2.3', '172.16.0.1', '172.31.255.255', '192.168.1.1', '169.254.169.254', '100.64.0.1', '0.0.0.0', '224.0.0.1', '::1', '::', 'fe80::1', 'fc00::1', 'fd12:3456::1', '::ffff:127.0.0.1', '::ffff:10.0.0.1'])('%s 不是公网地址', (address) => { expect(isPublicAddress(address)).toBe(false) }) it.each(['8.8.8.8', '1.1.1.1', '172.32.0.1', '93.184.216.34', '2606:4700:4700::1111'])('%s 是公网地址', (address) => { expect(isPublicAddress(address)).toBe(true) }) it('不是 IP 直接 throw', () => { expect(() => isPublicAddress('example.com')).toThrow(/not an IP address/) }) })