From faf61076538bd7a83ee58b4c1536a3a187b90688 Mon Sep 17 00:00:00 2001 From: aiirvizionz Date: Tue, 21 Jul 2026 19:01:31 -0600 Subject: [PATCH] fix(dns-porkbun): reject non-decimal ttl strings --- packages/dns/porkbun/src/index.test.ts | 26 ++++++++++++++++++++++++++ packages/dns/porkbun/src/index.ts | 15 +++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/dns/porkbun/src/index.test.ts b/packages/dns/porkbun/src/index.test.ts index 5fe97f33..2e978985 100644 --- a/packages/dns/porkbun/src/index.test.ts +++ b/packages/dns/porkbun/src/index.test.ts @@ -55,6 +55,32 @@ describe('Porkbun DNS API adapter', () => { ]); }); + it('falls back for non-decimal Porkbun TTL strings', async () => { + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + text: async () => JSON.stringify({ + status: 'SUCCESS', + records: [ + { id: 'sci', name: 'sci.example.com', type: 'A', content: '1.2.3.4', ttl: '1e2' }, + { id: 'hex', name: 'hex.example.com', type: 'A', content: '1.2.3.5', ttl: '0x10' }, + { id: 'decimal', name: 'decimal.example.com', type: 'A', content: '1.2.3.6', ttl: '600.5' }, + { id: 'normal', name: 'normal.example.com', type: 'A', content: '1.2.3.7', ttl: '600' }, + ], + }), + }); + vi.stubGlobal('fetch', fetchMock); + + await dns.connect(ctx(), {}); + const records = await dns.listRecords('example.com', { defaultTtl: 900 }); + + expect(records.map((record) => [record.id, record.ttl])).toEqual([ + ['sci', 900], + ['hex', 900], + ['decimal', 900], + ['normal', 600], + ]); + }); + it('lists API-enabled domains as zones', async () => { const fetchMock = vi.fn().mockResolvedValue({ ok: true, diff --git a/packages/dns/porkbun/src/index.ts b/packages/dns/porkbun/src/index.ts index 960a0251..7a5e184d 100644 --- a/packages/dns/porkbun/src/index.ts +++ b/packages/dns/porkbun/src/index.ts @@ -70,15 +70,26 @@ function ttlValue(ttl: number | undefined, config: Config): number { return ttl ?? config.defaultTtl ?? 600; } +function parsePorkbunTtl(ttl: PorkbunRecord['ttl']): number | undefined { + if (typeof ttl === 'number') { + return Number.isInteger(ttl) && ttl > 0 ? ttl : undefined; + } + if (typeof ttl !== 'string' || !/^[1-9]\d*$/.test(ttl)) { + return undefined; + } + const parsed = Number(ttl); + return Number.isSafeInteger(parsed) ? parsed : undefined; +} + function mapRecord(zoneId: string, record: PorkbunRecord, config: Config): DnsRecord { - const ttl = Number(record.ttl); + const ttl = parsePorkbunTtl(record.ttl); return { id: String(record.id), zone: zoneId, name: normalizeRecordName(zoneId, record.name), type: record.type as DnsRecord['type'], value: record.content, - ttl: Number.isFinite(ttl) && ttl > 0 ? ttl : ttlValue(undefined, config), + ttl: ttl ?? ttlValue(undefined, config), }; }