Skip to main content
Glama
secure-logger.test.ts6.1 kB
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest'; import { performance } from 'node:perf_hooks'; import { createScopedLogger, SecureLogger, safeMcpLog, OperationType, } from '@/utils/logger.js'; import { maskIdentifier, sanitizeErrorDetail, sanitizeLogMessage, sanitizeLogPayload, } from '@/utils/log-sanitizer.js'; describe('SecureLogger', () => { beforeEach(() => { process.env.LOG_FORMAT = 'json'; }); afterEach(() => { delete process.env.LOG_FORMAT; }); it('returns a SecureLogger instance from createScopedLogger', () => { const logger = createScopedLogger('secure-test'); expect(logger).toBeInstanceOf(SecureLogger); }); it('sanitizes sensitive structured data before logging', () => { const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); const logger = createScopedLogger( 'secure-test', 'login', OperationType.API_CALL ); logger.info('User login', { email: 'jane@example.com', token: 'secret_key_test_value_for_sanitization', session_id: '1234567890abcdef', profile: { phone: '+1 (555) 000-1111', }, }); expect(spy).toHaveBeenCalled(); const [payload] = spy.mock.calls.at(-1) ?? []; expect(typeof payload).toBe('string'); const parsed = JSON.parse(payload as string); expect(parsed.message).toBe('User login'); expect(parsed.metadata.module).toBe('secure-test'); expect(parsed.data.email).toBe('[EMAIL_REDACTED]'); expect(parsed.data.token).toBe('[TOKEN_REDACTED]'); expect(parsed.data.session_id).toMatch(/…/); expect(parsed.data.profile.phone).toBe('[PHONE_REDACTED]'); spy.mockRestore(); }); it('sanitizes error messages and details', () => { const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); const logger = createScopedLogger('secure-test'); const error = new Error('Token leaked: abcdefghijklmnopqrstuvwxyz012345'); (error as Error & { code?: string }).code = 'E_TOKEN'; logger.error('Operation failed', error, { authorization: 'Bearer abcdefghijklmnopqrstuvwxyz0123456789', }); const [payload] = spy.mock.calls.at(-1) ?? []; const parsed = JSON.parse(payload as string); expect(parsed.error.message).not.toContain( 'abcdefghijklmnopqrstuvwxyz012345' ); expect(parsed.error.code).toBe('E_TOKEN'); expect(parsed.data.authorization).toBe('Bearer [TOKEN_REDACTED]'); spy.mockRestore(); }); it('tracks operations with sanitized lifecycle helpers', () => { const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); const logger = createScopedLogger( 'secure-test', 'batch-run', OperationType.API_CALL ); const timer = logger.operationStart( 'fetch-records', OperationType.API_CALL, { authorization: 'Bearer secret-token', } ); const duration = timer.end('completed', { count: 2 }); logger.operationSuccess( 'fetch-records', { count: 2 }, OperationType.API_CALL, duration ); logger.operationFailure( 'fetch-records', new Error('User email leak: admin@example.com'), { email: 'admin@example.com' }, OperationType.API_CALL, duration ); logger.warn('Sensitive debug', { password: 'hunter2' }); expect(errorSpy).toHaveBeenCalled(); const failurePayload = JSON.parse( errorSpy.mock.calls.at(-1)?.[0] as string ); expect(failurePayload.error.message).not.toContain('admin@example.com'); expect(failurePayload.data.email).toBe('[EMAIL_REDACTED]'); expect(warnSpy).toHaveBeenCalled(); const warnPayload = JSON.parse(warnSpy.mock.calls.at(-1)?.[0] as string); expect(warnPayload.data.password).toBe('[REDACTED]'); errorSpy.mockRestore(); warnSpy.mockRestore(); }); it('keeps log serialization performant', () => { const payload = { id: '1234567890abcdef', nested: Array.from({ length: 10 }, (_, idx) => ({ token: `token-${idx}-${'x'.repeat(24)}`, profile: { email: `user${idx}@example.com`, phone: '+15550001111', }, })), }; const iterations = 200; const start = performance.now(); for (let i = 0; i < iterations; i += 1) { sanitizeLogPayload(payload); } const average = (performance.now() - start) / iterations; expect(average).toBeLessThan(5); }); it('sanitizes console safe logs', () => { const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); safeMcpLog('Bearer secret', { apiKey: 'abc1234567890abcdef123456' }); const [payload, data] = spy.mock.calls.at(-1) ?? []; expect(payload).toBe('[MCP_SAFE_LOG] Bearer [TOKEN_REDACTED]'); expect((data as { apiKey: string }).apiKey).toBe('[TOKEN_REDACTED]'); spy.mockRestore(); }); }); describe('Log sanitizer utilities', () => { it('masks identifiers consistently', () => { expect(maskIdentifier('1234567890')).toBe('1234…7890'); expect(maskIdentifier('abcd')).toBe('[ID_REDACTED]'); }); it('handles circular references safely', () => { const payload: { token: string; self?: unknown } = { token: 'token-value-abcdefghijklmnop', }; payload.self = payload; const sanitized = sanitizeLogPayload(payload) as { token: string; self: unknown; }; expect(sanitized.token).toBe('[TOKEN_REDACTED]'); expect(sanitized.self).toBeDefined(); }); it('sanitizes arbitrary error objects', () => { const detail = sanitizeErrorDetail({ message: 'apiKey=abcd1234', email: 'example@example.com', }); expect(detail?.message).not.toContain('abcd1234'); expect(detail?.details?.email).toBe('[EMAIL_REDACTED]'); }); it('redacts sensitive text in messages', () => { const sanitized = sanitizeLogMessage( 'Bearer abcdefghijklmnopqrstuvwxyz0123' ); expect(sanitized).toBe('Bearer [TOKEN_REDACTED]'); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server