104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { scrubString, scrubValue } from '@/lib/observability/scrub'
|
|
import { parseDsn, buildEvent } from '@/lib/observability/capture'
|
|
|
|
describe('scrubString', () => {
|
|
it('redacts email addresses', () => {
|
|
expect(scrubString('to [email protected] now')).toBe('to [email] now')
|
|
})
|
|
|
|
it('redacts stripe secret + restricted keys and webhook secret', () => {
|
|
expect(scrubString('sk_test_abc123XYZ')).toBe('[stripe_key]')
|
|
expect(scrubString('sk_live_DEADbeef00')).toBe('[stripe_key]')
|
|
expect(scrubString('rk_test_zzz999')).toBe('[stripe_key]')
|
|
expect(scrubString('whsec_qWErTy123')).toBe('[stripe_whsec]')
|
|
})
|
|
|
|
it('redacts a Resend key', () => {
|
|
expect(scrubString('re_AbCd1234efgh')).toBe('[resend_key]')
|
|
})
|
|
|
|
it('redacts JWTs (supabase keys) and bearer tokens', () => {
|
|
expect(scrubString('eyJhbG.eyJzdWIiOiIx.sIgnAtuRe-_0')).toBe('[jwt]')
|
|
expect(scrubString('Authorization: Bearer abc.def_123')).toBe('Authorization: Bearer [redacted]')
|
|
})
|
|
|
|
it('redacts high-entropy hex but leaves UUIDs intact', () => {
|
|
expect(scrubString('key=0123456789abcdef0123456789abcdef')).toBe('key=[hex]')
|
|
const uuid = 'f56d5f3a-8877-4d56-a6bc-c572474935a8'
|
|
expect(scrubString(`space ${uuid}`)).toBe(`space ${uuid}`)
|
|
})
|
|
|
|
it('leaves benign text unchanged', () => {
|
|
expect(scrubString('handler error: timeout after 6s')).toBe('handler error: timeout after 6s')
|
|
})
|
|
})
|
|
|
|
describe('scrubValue', () => {
|
|
it('deep-walks objects and arrays', () => {
|
|
const out = scrubValue({ a: '[email protected]', b: { c: ['re_abcd1234', 'ok'] } })
|
|
expect(out).toEqual({ a: '[email]', b: { c: ['[resend_key]', 'ok'] } })
|
|
})
|
|
|
|
it('breaks cycles without throwing', () => {
|
|
const o: Record<string, unknown> = { name: 'safe' }
|
|
o.self = o
|
|
const out = scrubValue(o) as Record<string, unknown>
|
|
expect(out.name).toBe('safe')
|
|
expect(out.self).toBe('[circular]')
|
|
})
|
|
|
|
it('passes through non-string scalars', () => {
|
|
expect(scrubValue({ n: 5, b: true, z: null })).toEqual({ n: 5, b: true, z: null })
|
|
})
|
|
})
|
|
|
|
describe('parseDsn', () => {
|
|
it('parses a well-formed DSN into a store URL', () => {
|
|
const p = parseDsn('https://[email protected]/7')
|
|
expect(p).toEqual({
|
|
key: 'pubkey',
|
|
host: 'glitchtip.example.com',
|
|
projectId: '7',
|
|
storeUrl: 'https://glitchtip.example.com/api/7/store/',
|
|
})
|
|
})
|
|
|
|
it('returns null for empty/garbage/missing parts', () => {
|
|
expect(parseDsn(undefined)).toBeNull()
|
|
expect(parseDsn('')).toBeNull()
|
|
expect(parseDsn('not a url')).toBeNull()
|
|
expect(parseDsn('https://glitchtip.example.com/7')).toBeNull() // no key
|
|
expect(parseDsn('https://[email protected]/')).toBeNull() // no project
|
|
})
|
|
})
|
|
|
|
describe('buildEvent', () => {
|
|
it('builds an exception event, scrubbing message + stack', () => {
|
|
const err = new Error('charge failed for [email protected]')
|
|
err.stack = 'Error: charge failed for [email protected]\n at x (sk_test_leak123)'
|
|
const ev = buildEvent({ error: err }, { surface: 'stripe-webhook', tags: { space: 'abc' } }, 1_700_000_000_000)
|
|
expect(ev.platform).toBe('node')
|
|
expect(ev.level).toBe('error')
|
|
expect(ev.timestamp).toBe(1_700_000_000)
|
|
expect(ev.transaction).toBe('stripe-webhook')
|
|
expect(ev.tags).toEqual({ space: 'abc' })
|
|
expect(ev.exception?.values[0].type).toBe('Error')
|
|
expect(ev.exception?.values[0].value).toBe('charge failed for [email]')
|
|
expect(ev.exception?.values[0].stacktrace_raw).toContain('[email]')
|
|
expect(ev.exception?.values[0].stacktrace_raw).toContain('[stripe_key]')
|
|
expect(ev.event_id).toMatch(/^[0-9a-f]{32}$/)
|
|
})
|
|
|
|
it('builds a message event and scrubs extra', () => {
|
|
const ev = buildEvent(
|
|
{ message: 'prefs lookup failed' },
|
|
{ level: 'warning', extra: { recipient: '[email protected]' } },
|
|
)
|
|
expect(ev.message).toBe('prefs lookup failed')
|
|
expect(ev.level).toBe('warning')
|
|
expect(ev.extra).toEqual({ recipient: '[email]' })
|
|
expect(ev.exception).toBeUndefined()
|
|
})
|
|
})
|