import { walkAndMask } from '.'; describe('Masking Functions', () => { describe('walkAndMask', () => { it('should mask sensitive headers by name regardless of content', () => { const node = { headers: { 'Authorization': 'Bearer any-token-here', 'X-API-Key': 'not-a-secret-value', 'Cookie': 'session=12345', 'Content-Type': 'application/json' } }; const masked = walkAndMask(node, { secretValues: [] }); // Sensitive headers should be masked by name, not by content expect(masked.headers.Authorization).toBe('Bearer ********'); expect(masked.headers['X-API-Key']).toBe('********'); expect(masked.headers.Cookie).toBe('********'); // Non-sensitive headers should remain unchanged expect(masked.headers['Content-Type']).toBe('application/json'); }); it('should preserve Authorization scheme while masking credentials', () => { const node = { headers: { 'Authorization': 'Bearer secret-bearer-token-123', 'Proxy-Authorization': 'Basic dGVzdHVzZXI6cGFzc3dvcmQ=' } }; const masked = walkAndMask(node, { secretValues: [] }); // Authorization schemes should be preserved expect(masked.headers.Authorization).toBe('Bearer ********'); expect(masked.headers['Proxy-Authorization']).toBe('Basic ********'); }); it('should mask sensitive headers containing secrets while preserving structure', () => { const node = { headers: { 'Set-Cookie': 'session=secret-session-abc; path=/', 'X-Custom-Auth': 'Bearer secret-token-456' } }; const masked = walkAndMask(node, { secretValues: ['secret-session-abc', 'secret-token-456'] }); // Should preserve structure while masking secrets expect(masked.headers['Set-Cookie']).toBe('session=********; path=/'); expect(masked.headers['X-Custom-Auth']).toBe('Bearer ********'); }); it('should apply value-based masking to non-sensitive data', () => { const node = { request: { url: 'https://api.example.com/auth?token=secret-token-123&user=john', data: { username: 'john', password: 'secret-password-789' } } }; const masked = walkAndMask(node, { secretValues: ['secret-token-123', 'secret-password-789'] }); // URLs should preserve structure while masking secrets expect(masked.request.url).toBe( 'https://api.example.com/auth?token=********&user=john' ); // JSON data should preserve structure while masking secrets expect(masked.request.data.username).toBe('john'); expect(masked.request.data.password).toBe('********'); }); it('should handle arrays correctly', () => { const node = [ { name: 'user1', token: 'secret-token-1' }, { name: 'user2', token: 'secret-token-2' } ]; const masked = walkAndMask(node, { secretValues: ['secret-token-1', 'secret-token-2'] }); expect(masked).toEqual([ { name: 'user1', token: '********' }, { name: 'user2', token: '********' } ]); }); it('should handle nested objects correctly', () => { const node = { user: { profile: { name: 'john', password: 'secret-password-123' }, settings: { apiKey: 'secret-api-key-456' } } }; const masked = walkAndMask(node, { secretValues: ['secret-password-123', 'secret-api-key-456'] }); expect(masked.user.profile.password).toBe('********'); expect(masked.user.settings.apiKey).toBe('********'); expect(masked.user.profile.name).toBe('john'); }); it('should handle mixed data types correctly', () => { const node = { string: 'hello world', number: 42, boolean: true, null: null, array: [1, 2, 3], object: { key: 'value' }, secret: 'secret-value-123' }; const masked = walkAndMask(node, { secretValues: ['secret-value-123'] }); expect(masked.string).toBe('hello world'); expect(masked.number).toBe(42); expect(masked.boolean).toBe(true); expect(masked.null).toBeNull(); expect(masked.array).toEqual([1, 2, 3]); expect(masked.object).toEqual({ key: 'value' }); expect(masked.secret).toBe('********'); }); it('should handle null/undefined values gracefully', () => { expect(walkAndMask(null)).toBeNull(); expect(walkAndMask(undefined)).toBeUndefined(); expect(walkAndMask({})).toEqual({}); }); it('should not modify original data', () => { const original = { request: { headers: { Authorization: 'Bearer secret-token-123' }, data: { password: 'secret-password-456' } } }; const originalCopy = JSON.parse(JSON.stringify(original)); walkAndMask(original, { secretValues: ['secret-token-123', 'secret-password-456'] }); expect(original).toEqual(originalCopy); }); it('should handle complex nested structures with mixed sensitive data', () => { const node = { api: { requests: [ { method: 'POST', url: 'https://api.com/auth?client_id=secret-client&scope=read', headers: { 'Authorization': 'Bearer secret-bearer-token', 'X-API-Key': 'secret-api-key-123', 'Content-Type': 'application/json' }, body: { username: 'john', password: 'secret-password-789', metadata: { apiKey: 'secret-metadata-key' } } } ] } }; const masked = walkAndMask(node, { secretValues: [ 'secret-client', 'secret-bearer-token', 'secret-api-key-123', 'secret-password-789', 'secret-metadata-key' ] }); // URL should preserve structure expect(masked.api.requests[0].url).toBe( 'https://api.com/auth?client_id=********&scope=read' ); // Sensitive headers should be masked by name expect(masked.api.requests[0].headers.Authorization).toBe( 'Bearer ********' ); expect(masked.api.requests[0].headers['X-API-Key']).toBe('********'); // Non-sensitive headers should remain expect(masked.api.requests[0].headers['Content-Type']).toBe( 'application/json' ); // Body data should preserve structure while masking secrets expect(masked.api.requests[0].body.username).toBe('john'); expect(masked.api.requests[0].body.password).toBe('********'); expect(masked.api.requests[0].body.metadata.apiKey).toBe('********'); }); it('should handle regex special characters in secrets correctly', () => { const node = { data: { token: 'secret.token+123', apiKey: 'secret[api]key', password: 'secret$password^456' } }; const masked = walkAndMask(node, { secretValues: [ 'secret.token+123', 'secret[api]key', 'secret$password^456' ] }); expect(masked.data.token).toBe('********'); expect(masked.data.apiKey).toBe('********'); expect(masked.data.password).toBe('********'); }); }); });