/** @jest-environment jsdom */ import { MAX_ATTRIBUTE_KEYS, MAX_TAG_KEYS, isUnsafeKey } from '../../core/limits'; import { assignRecord, copyRecord, emptyRecord, sanitizeJourneyEvent } from '../../core/sanitize'; import type { JourneyEvent } from '../../core/types'; describe('[journey] sanitize / copyRecord', () => { test('isUnsafeKey blocks proto and secret names', () => { expect(isUnsafeKey('__proto__')).toBe(true); expect(isUnsafeKey('constructor')).toBe(true); expect(isUnsafeKey('Authorization')).toBe(true); expect(isUnsafeKey('token')).toBe(true); expect(isUnsafeKey('customer.id')).toBe(false); }); test('JSON __proto__ assign does not pollute Object.prototype', () => { const target = emptyRecord(); const polluted = JSON.parse('{"__proto__":{"polluted":true},"ok":1}') as Record< string, unknown >; assignRecord(target, polluted, MAX_TAG_KEYS); expect(target.ok).toBe(1); expect(({} as { polluted?: boolean }).polluted).toBeUndefined(); expect(Object.prototype.hasOwnProperty.call(target, '__proto__')).toBe(false); }); test('copyRecord caps keys and drops secrets', () => { const src: Record = { token: 'secret', keep: 1 }; for (let i = 0; i < MAX_ATTRIBUTE_KEYS + 5; i += 1) { src[`k${i}`] = i; } const out = copyRecord(src, { maxKeys: MAX_ATTRIBUTE_KEYS, normalizeKeys: true }); expect(out.token).toBeUndefined(); expect(Object.keys(out).length).toBe(MAX_ATTRIBUTE_KEYS); }); test('sanitizeJourneyEvent normalizes step names and tag keys', () => { const event: JourneyEvent = { journey: { name: 'Make A Sale!!', team: 'Sales', group: 'retail', service: 'checkout', outcome: 'good', durationMs: 10, expected: ['Create Sale'], tags: { 'User Email': 'a@b.com', 'token': 'nope', 'plan': 'Pro' }, steps: [ { name: 'Load Customer 1', startMs: 0, durationMs: 4, outcome: 'good', attributes: { 'token': 'x', 'Draft.id': 'abc' }, }, ], }, }; const j = sanitizeJourneyEvent(event).journey; expect(j.name).toBe('make_a_sale'); expect(j.expected).toEqual(['create_sale']); expect(j.steps[0].name).toBe('load_customer_1'); expect(j.tags?.user_email).toBe('a_b.com'); expect(j.tags?.token).toBeUndefined(); expect(j.tags?.plan).toBe('pro'); expect(j.steps[0].attributes?.token).toBeUndefined(); expect(j.steps[0].attributes?.['draft.id']).toBe('abc'); }); });