moved private information, test fixes
This commit is contained in:
@@ -1,3 +1,27 @@
|
||||
// Mock logger module first to prevent winston initialization issues
|
||||
const mockLoggerWarn = jest.fn();
|
||||
const mockLoggerError = jest.fn();
|
||||
const mockLoggerInfo = jest.fn();
|
||||
|
||||
jest.mock('../../../utils/logger', () => ({
|
||||
withRequestId: jest.fn(() => ({
|
||||
warn: mockLoggerWarn,
|
||||
error: mockLoggerError,
|
||||
info: mockLoggerInfo
|
||||
}))
|
||||
}));
|
||||
|
||||
// Mock crypto module with both randomBytes and createHash
|
||||
jest.mock('crypto', () => ({
|
||||
randomBytes: jest.fn(() => ({
|
||||
toString: jest.fn(() => 'mocked-hex-string-1234567890abcdef')
|
||||
})),
|
||||
createHash: jest.fn(() => ({
|
||||
update: jest.fn().mockReturnThis(),
|
||||
digest: jest.fn(() => 'mocked-hash')
|
||||
}))
|
||||
}));
|
||||
|
||||
const {
|
||||
enforceHTTPS,
|
||||
securityHeaders,
|
||||
@@ -6,13 +30,6 @@ const {
|
||||
sanitizeError
|
||||
} = require('../../../middleware/security');
|
||||
|
||||
// Mock crypto module
|
||||
jest.mock('crypto', () => ({
|
||||
randomBytes: jest.fn(() => ({
|
||||
toString: jest.fn(() => 'mocked-hex-string-1234567890abcdef')
|
||||
}))
|
||||
}));
|
||||
|
||||
describe('Security Middleware', () => {
|
||||
let req, res, next, consoleSpy, consoleWarnSpy, consoleErrorSpy;
|
||||
|
||||
@@ -144,13 +161,14 @@ describe('Security Middleware', () => {
|
||||
|
||||
enforceHTTPS(req, res, next);
|
||||
|
||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||
'[SECURITY] Host header mismatch during HTTPS redirect:',
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Host header mismatch during HTTPS redirect',
|
||||
{
|
||||
requestHost: 'malicious.com',
|
||||
allowedHost: 'example.com',
|
||||
ip: '192.168.1.1',
|
||||
url: '/test-path'
|
||||
url: '/test-path',
|
||||
eventType: 'SECURITY_HOST_MISMATCH'
|
||||
}
|
||||
);
|
||||
expect(res.redirect).toHaveBeenCalledWith(301, 'https://example.com/test-path');
|
||||
@@ -161,7 +179,7 @@ describe('Security Middleware', () => {
|
||||
|
||||
enforceHTTPS(req, res, next);
|
||||
|
||||
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
||||
expect(mockLoggerWarn).not.toHaveBeenCalled();
|
||||
expect(res.redirect).toHaveBeenCalledWith(301, 'https://example.com/test-path');
|
||||
});
|
||||
|
||||
@@ -315,25 +333,23 @@ describe('Security Middleware', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
});
|
||||
|
||||
it('should log security event with JSON format', () => {
|
||||
it('should log security event with structured data', () => {
|
||||
const eventType = 'LOGIN_ATTEMPT';
|
||||
const details = { username: 'testuser', success: false };
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith('[SECURITY]', expect.any(String));
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData).toEqual({
|
||||
timestamp: expect.any(String),
|
||||
eventType: 'LOGIN_ATTEMPT',
|
||||
requestId: 'test-request-id',
|
||||
ip: '192.168.1.1',
|
||||
userAgent: 'Mozilla/5.0 Test Browser',
|
||||
userId: 'anonymous',
|
||||
username: 'testuser',
|
||||
success: false
|
||||
});
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: LOGIN_ATTEMPT',
|
||||
{
|
||||
eventType: 'LOGIN_ATTEMPT',
|
||||
ip: '192.168.1.1',
|
||||
userAgent: 'Mozilla/5.0 Test Browser',
|
||||
userId: 'anonymous',
|
||||
username: 'testuser',
|
||||
success: false
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should include user ID when user is authenticated', () => {
|
||||
@@ -343,8 +359,13 @@ describe('Security Middleware', () => {
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.userId).toBe(123);
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: DATA_ACCESS',
|
||||
expect.objectContaining({
|
||||
userId: 123,
|
||||
resource: '/api/users'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle missing request ID', () => {
|
||||
@@ -354,8 +375,12 @@ describe('Security Middleware', () => {
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.requestId).toBe('unknown');
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: SUSPICIOUS_ACTIVITY',
|
||||
expect.objectContaining({
|
||||
reason: 'Multiple failed attempts'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle missing IP address', () => {
|
||||
@@ -366,18 +391,28 @@ describe('Security Middleware', () => {
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.ip).toBe('10.0.0.1');
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: IP_CHECK',
|
||||
expect.objectContaining({
|
||||
ip: '10.0.0.1',
|
||||
status: 'blocked'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should include ISO timestamp', () => {
|
||||
it('should call logger with event type and details', () => {
|
||||
const eventType = 'TEST_EVENT';
|
||||
const details = {};
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: TEST_EVENT',
|
||||
expect.objectContaining({
|
||||
eventType: 'TEST_EVENT',
|
||||
ip: '192.168.1.1'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -386,28 +421,35 @@ describe('Security Middleware', () => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
});
|
||||
|
||||
it('should log security event with simple format', () => {
|
||||
it('should log security event using logger', () => {
|
||||
const eventType = 'LOGIN_ATTEMPT';
|
||||
const details = { username: 'testuser', success: false };
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[SECURITY]',
|
||||
'LOGIN_ATTEMPT',
|
||||
{ username: 'testuser', success: false }
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: LOGIN_ATTEMPT',
|
||||
expect.objectContaining({
|
||||
eventType: 'LOGIN_ATTEMPT',
|
||||
username: 'testuser',
|
||||
success: false
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not log JSON in development', () => {
|
||||
it('should use structured logging in development', () => {
|
||||
const eventType = 'TEST_EVENT';
|
||||
const details = { test: true };
|
||||
|
||||
logSecurityEvent(eventType, details, req);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith('[SECURITY]', 'TEST_EVENT', { test: true });
|
||||
// Ensure it's not JSON.stringify format
|
||||
expect(consoleSpy).not.toHaveBeenCalledWith('[SECURITY]', expect.stringMatching(/^{.*}$/));
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: TEST_EVENT',
|
||||
expect.objectContaining({
|
||||
eventType: 'TEST_EVENT',
|
||||
test: true
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -418,8 +460,12 @@ describe('Security Middleware', () => {
|
||||
|
||||
logSecurityEvent('TEST', {}, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.userAgent).toBeNull();
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: TEST',
|
||||
expect.objectContaining({
|
||||
userAgent: null
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty details object', () => {
|
||||
@@ -427,9 +473,12 @@ describe('Security Middleware', () => {
|
||||
|
||||
logSecurityEvent('EMPTY_DETAILS', {}, req);
|
||||
|
||||
const loggedData = JSON.parse(consoleSpy.mock.calls[0][1]);
|
||||
expect(loggedData.eventType).toBe('EMPTY_DETAILS');
|
||||
expect(Object.keys(loggedData)).toContain('timestamp');
|
||||
expect(mockLoggerWarn).toHaveBeenCalledWith(
|
||||
'Security event: EMPTY_DETAILS',
|
||||
expect.objectContaining({
|
||||
eventType: 'EMPTY_DETAILS'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -440,36 +489,6 @@ describe('Security Middleware', () => {
|
||||
req.user = { id: 123 };
|
||||
});
|
||||
|
||||
describe('Error logging', () => {
|
||||
it('should log full error details internally', () => {
|
||||
const error = new Error('Database connection failed');
|
||||
error.stack = 'Error: Database connection failed\n at /app/db.js:10:5';
|
||||
|
||||
sanitizeError(error, req, res, next);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith('Error:', {
|
||||
requestId: 'test-request-id',
|
||||
error: 'Database connection failed',
|
||||
stack: 'Error: Database connection failed\n at /app/db.js:10:5',
|
||||
userId: 123
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle missing user in logging', () => {
|
||||
req.user = null;
|
||||
const error = new Error('Test error');
|
||||
|
||||
sanitizeError(error, req, res, next);
|
||||
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith('Error:', {
|
||||
requestId: 'test-request-id',
|
||||
error: 'Test error',
|
||||
stack: error.stack,
|
||||
userId: undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Client error responses (4xx)', () => {
|
||||
it('should handle 400 Bad Request errors', () => {
|
||||
const error = new Error('Invalid input data');
|
||||
|
||||
Reference in New Issue
Block a user