import { describe, it, expect } from 'vitest';
import { sanitizeError } from './errorSanitizer.js';
describe('Error Sanitization', () => {
    it('should redact JWT tokens from error messages', () => {
        const error = new Error('Invalid token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
        const sanitized = sanitizeError(error);
        expect(sanitized.message).toBe('Invalid token: [REDACTED]');
        expect(sanitized.message).not.toContain('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');
    });
    it('should redact bearer tokens from error messages', () => {
        const error = new Error('Authentication failed with bearer abc123token456');
        const sanitized = sanitizeError(error);
        expect(sanitized.message).toBe('Authentication failed with bearer [REDACTED]');
    });
    it('should redact API keys from error messages', () => {
        const error = new Error('API request failed: api_key=sk_live_abc123def456');
        const sanitized = sanitizeError(error);
        expect(sanitized.message).toBe('API request failed: api_key=[REDACTED]');
    });
    it('should remove sensitive fields from error objects', () => {
        const error = new Error('Test error');
        error.token = 'secret-token-123';
        error.apiKey = 'api-key-456';
        error.password = 'mypassword';
        error.requestData = {
            username: 'john',
            authorization: 'Bearer token123'
        };
        const sanitized = sanitizeError(error);
        expect(sanitized.token).toBe('[REDACTED]');
        expect(sanitized.apiKey).toBe('[REDACTED]');
        expect(sanitized.password).toBe('[REDACTED]');
        expect(sanitized.requestData.username).toBe('john'); // Non-sensitive field preserved
        expect(sanitized.requestData.authorization).toBe('Bearer [REDACTED]');
    });
    it('should sanitize stack traces', () => {
        const error = new Error('Test error');
        error.stack = `Error: Invalid token eyJhbGciOiJIUzI1NiI...
    at AuthService.validateToken (/app/src/auth.js:42:15)
    at api_key=sk_live_abc123 (/app/src/api.js:20:10)`;
        const sanitized = sanitizeError(error);
        expect(sanitized.stack).toContain('[REDACTED]');
        expect(sanitized.stack).not.toContain('eyJhbGciOiJIUzI1NiI');
        expect(sanitized.stack).not.toContain('sk_live_abc123');
    });
    it('should preserve error type and non-sensitive properties', () => {
        const error = new TypeError('Invalid token: abc123def456ghi789');
        error.statusCode = 401;
        error.userId = 'user-123';
        const sanitized = sanitizeError(error);
        expect(sanitized.name).toBe('TypeError');
        expect(sanitized.statusCode).toBe(401);
        expect(sanitized.userId).toBe('user-123');
        expect(sanitized.message).toBe('Invalid token: [REDACTED]');
    });
    it('should handle non-Error objects', () => {
        const errorObj = {
            message: 'Auth failed with token abc123',
            token: 'secret-token',
            data: {
                user: 'john',
                password: 'secret123'
            }
        };
        const sanitized = sanitizeError(errorObj);
        expect(sanitized.message).toBe('Auth failed with token [REDACTED]');
        expect(sanitized.token).toBe('[REDACTED]');
        expect(sanitized.data.user).toBe('john');
        expect(sanitized.data.password).toBe('[REDACTED]');
    });
    it('should not double-sanitize already sanitized errors', () => {
        const error = new Error('Invalid token: eyJhbGciOiJIUzI1NiI...');
        const firstSanitization = sanitizeError(error);
        const secondSanitization = sanitizeError(firstSanitization);
        expect(firstSanitization.message).toBe(secondSanitization.message);
        expect(firstSanitization._sanitized).toBe(true);
    });
    it('should handle null and undefined gracefully', () => {
        expect(sanitizeError(null)).toBe(null);
        expect(sanitizeError(undefined)).toBe(undefined);
    });
    describe('Headers Sanitization', () => {
        it('should sanitize authorization headers while preserving structure', () => {
            const error = new Error('Request failed');
            error.headers = {
                authorization: 'Bearer eyJhbGciOiJIUzI1NiI...',
                'content-type': 'application/json',
                'x-api-key': 'sk_live_abc123'
            };
            const sanitized = sanitizeError(error);
            expect(sanitized.headers.authorization).toBe('Bearer [REDACTED]');
            expect(sanitized.headers['content-type']).toBe('application/json'); // Non-sensitive preserved
            expect(sanitized.headers['x-api-key']).toBe('[REDACTED]'); // Contains 'key' -> completely redacted
        });
        it('should sanitize headers in HTTP request/response objects', () => {
            const error = new Error('HTTP request failed');
            error.request = {
                url: 'https://api.example.com/users',
                method: 'GET',
                headers: {
                    Authorization: 'Bearer token123',
                    Cookie: 'session=abc123; auth_token=xyz789',
                    'User-Agent': 'MyApp/1.0'
                }
            };
            error.response = {
                status: 401,
                headers: {
                    'WWW-Authenticate': 'Bearer realm="api"',
                    'Set-Cookie': 'refresh_token=def456; HttpOnly',
                    'Content-Type': 'application/json'
                }
            };
            const sanitized = sanitizeError(error);
            // Request headers
            expect(sanitized.request.headers.Authorization).toBe('Bearer [REDACTED]');
            expect(sanitized.request.headers.Cookie).toBe('[REDACTED]'); // Contains 'cookie' -> completely redacted
            expect(sanitized.request.headers['User-Agent']).toBe('MyApp/1.0');
            // Response headers
            expect(sanitized.response.headers['WWW-Authenticate']).toBe('[REDACTED]'); // Contains 'bearer' pattern -> redacted
            expect(sanitized.response.headers['Set-Cookie']).toBe('[REDACTED]'); // Contains 'cookie' -> completely redacted
            expect(sanitized.response.headers['Content-Type']).toBe('application/json');
        });
        it('should handle case-insensitive header names', () => {
            const error = new Error('Request failed');
            error.Headers = {
                // Capital H - this field name doesn't contain sensitive keywords
                AUTHORIZATION: 'Bearer token123',
                authorization: 'Basic user:pass',
                'X-API-KEY': 'secret-key'
            };
            const sanitized = sanitizeError(error);
            // Headers (capital H) is not in VALUE_SANITIZED_FIELDS, so individual fields get processed normally
            // But Basic auth pattern still applies for string value sanitization
            expect(sanitized.Headers.AUTHORIZATION).toBe('Bearer [REDACTED]'); // Pattern matching on string value
            expect(sanitized.Headers.authorization).toBe('Basic [REDACTED]'); // Basic auth pattern applies
            expect(sanitized.Headers['X-API-KEY']).toBe('[REDACTED]'); // Contains 'key' -> completely redacted
        });
        it('should sanitize headers with various token formats', () => {
            const errorObj = {
                message: 'Authentication failed',
                headers: {
                    authorization: 'JWT eyJhbGciOiJIUzI1NiI...',
                    'x-auth-token': 'Bearer abc123def456',
                    'api-key': 'sk_test_1234567890',
                    cookie: 'sessionId=sess_abc123; csrfToken=csrf_xyz789',
                    'x-forwarded-for': '192.168.1.1' // Should be preserved
                }
            };
            const sanitized = sanitizeError(errorObj);
            expect(sanitized.headers.authorization).toBe('JWT [REDACTED]');
            expect(sanitized.headers['x-auth-token']).toBe('[REDACTED]'); // Contains 'auth' -> completely redacted
            expect(sanitized.headers['api-key']).toBe('[REDACTED]'); // Contains 'key' -> completely redacted
            expect(sanitized.headers.cookie).toBe('[REDACTED]'); // Contains 'cookie' -> completely redacted
            expect(sanitized.headers['x-forwarded-for']).toBe('192.168.1.1');
        });
        it('should handle nested headers in complex objects', () => {
            const error = new Error('Network error');
            error.requestConfig = {
                baseURL: 'https://api.example.com',
                timeout: 5000,
                headers: {
                    Authorization: 'Bearer eyJhbGciOiJIUzI1NiI...',
                    'Content-Type': 'application/json'
                },
                data: {
                    username: 'john',
                    password: 'secret123'
                }
            };
            const sanitized = sanitizeError(error);
            expect(sanitized.requestConfig.baseURL).toBe('https://api.example.com');
            expect(sanitized.requestConfig.timeout).toBe(5000);
            expect(sanitized.requestConfig.headers.Authorization).toBe('Bearer [REDACTED]');
            expect(sanitized.requestConfig.headers['Content-Type']).toBe('application/json');
            expect(sanitized.requestConfig.data.username).toBe('john');
            expect(sanitized.requestConfig.data.password).toBe('[REDACTED]');
        });
        it('should handle headers as strings vs objects', () => {
            const error = new Error('Invalid headers');
            error.headers = 'Authorization: Bearer token123\r\nContent-Type: application/json';
            error.requestHeaders = {
                authorization: 'Basic dXNlcjpwYXNz', // base64 encoded user:pass
                'user-agent': 'MyApp/1.0'
            };
            const sanitized = sanitizeError(error);
            // String headers get pattern-based sanitization (Bearer preserves prefix)
            expect(sanitized.headers).toBe('Authorization: Bearer [REDACTED]\r\nContent-Type: application/json');
            // Any field named 'authorization' should be value-sanitized regardless of parent object name
            expect(sanitized.requestHeaders.authorization).toBe('Basic [REDACTED]');
            expect(sanitized.requestHeaders['user-agent']).toBe('MyApp/1.0');
        });
        it('should redact GitHub tokens', () => {
            const errorObj = {
                message: 'GitHub API failed',
                personalToken: 'ghp_1234567890abcdef1234567890abcdef12345678',
                appToken: 'ghs_abcdef1234567890abcdef1234567890abcdef12',
                finegrainedToken: 'github_pat_11ABCDEFG0001234567890_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
                config: {
                    auth: 'token ghp_1234567890abcdef1234567890abcdef12345678'
                }
            };
            const sanitized = sanitizeError(errorObj);
            expect(sanitized.personalToken).toBe('[REDACTED]');
            expect(sanitized.appToken).toBe('[REDACTED]');
            expect(sanitized.finegrainedToken).toBe('[REDACTED]');
            expect(sanitized.config.auth).toBe('[REDACTED]'); // "token <value>" pattern matches the whole thing
        });
        it('should handle mixed secrets in complex error objects', () => {
            const error = new Error('Multiple services failed');
            error.aws = {
                accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
                secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
                sessionToken: 'aws_session_token_12345abcdef'
            };
            error.github = {
                token: 'ghp_1234567890abcdef1234567890abcdef12345678'
            };
            error.nonSensitive = {
                userId: 'user123',
                timestamp: '2023-01-01T00:00:00Z'
            };
            const sanitized = sanitizeError(error);
            // AWS credentials should be redacted
            expect(sanitized.aws.accessKeyId).toBe('[REDACTED]');
            expect(sanitized.aws.secretAccessKey).toBe('[REDACTED]');
            expect(sanitized.aws.sessionToken).toBe('[REDACTED]');
            // GitHub token should be redacted
            expect(sanitized.github.token).toBe('[REDACTED]');
            // Non-sensitive data should be preserved
            expect(sanitized.nonSensitive.userId).toBe('user123');
            expect(sanitized.nonSensitive.timestamp).toBe('2023-01-01T00:00:00Z');
        });
    });
});
//# sourceMappingURL=errorSanitizer.test.js.map