UNPKG

qapinterface

Version:

Comprehensive API utilities for Node.js applications including authentication, security, request processing, and response handling with zero external dependencies

369 lines (310 loc) 14.3 kB
/** * Comprehensive unit tests for security modules * Tests all security-related functionality with mocked dependencies */ const { expect } = require('chai'); // Mock external dependencies const mockHelmet = { contentSecurityPolicy: { getDefaultDirectives: () => ({ 'default-src': ["'self'"], 'script-src': ["'self'"] }) } }; // Mock modules before requiring actual modules const originalRequire = require; // Mock helmet module - simplified for testing const helmet = () => mockHelmet; // Import modules to test const { createSecurityMiddleware } = require('../security'); const { detectSQLInjection } = require('../security/sql-injection-detector'); const { detectNoSQLInjection } = require('../security/nosql-injection-detector'); const { detectXSSAttempts } = require('../security/xss-detector'); const { detectPathTraversal } = require('../security/path-traversal-detector'); const { detectSuspiciousUserAgent } = require('../security/user-agent-analyzer'); const { analyzeSecurityPatterns } = require('../security/pattern-analyzer'); const { logSecurityEvent } = require('../security/event-logger'); const { generateSecureNonce } = require('../security/csp-nonce-generator'); const { createCSPHeader } = require('../security/csp-header-builder'); const { validateCSPNonce } = require('../security/csp-nonce-validator'); const { enforceHTTPS } = require('../security/https-enforcer'); // Restore require require = originalRequire; describe('Security Module Tests', () => { describe('SQL Injection Detection', () => { it('should detect basic SQL injection patterns', () => { expect(detectSQLInjection("'; DROP TABLE users; --")).to.equal(true); expect(detectSQLInjection("admin' OR '1'='1")).to.equal(true); expect(detectSQLInjection("UNION SELECT * FROM passwords")).to.equal(true); expect(detectSQLInjection("1; DELETE FROM accounts")).to.equal(true); }); it('should allow safe input', () => { expect(detectSQLInjection("normal user input")).to.equal(false); expect(detectSQLInjection("email@example.com")).to.equal(false); expect(detectSQLInjection("My name is John")).to.equal(false); expect(detectSQLInjection("Product ID: 12345")).to.equal(false); }); it('should handle edge cases', () => { expect(detectSQLInjection("")).to.equal(false); expect(detectSQLInjection(null)).to.equal(false); expect(detectSQLInjection(undefined)).to.equal(false); expect(detectSQLInjection(123)).to.equal(false); }); it('should detect case-insensitive patterns', () => { expect(detectSQLInjection("select * from users")).to.equal(true); expect(detectSQLInjection("SELECT * FROM USERS")).to.equal(true); expect(detectSQLInjection("SeLeCt * FrOm UsErS")).to.equal(true); }); }); describe('NoSQL Injection Detection', () => { it('should detect MongoDB injection patterns', () => { expect(detectNoSQLInjection('{"$where": "this.credits == this.debits"}')).to.equal(true); expect(detectNoSQLInjection('{"$regex": ".*"}')).to.equal(true); expect(detectNoSQLInjection('{"$ne": null}')).to.equal(true); expect(detectNoSQLInjection('{"$gt": ""}')).to.equal(true); }); it('should detect JavaScript injection in NoSQL', () => { expect(detectNoSQLInjection('function() { return true; }')).to.equal(true); expect(detectNoSQLInjection('this.password')).to.equal(true); expect(detectNoSQLInjection('sleep(5000)')).to.equal(true); }); it('should allow safe NoSQL queries', () => { expect(detectNoSQLInjection('{"name": "John Doe"}')).to.equal(false); expect(detectNoSQLInjection('{"age": 25}')).to.equal(false); expect(detectNoSQLInjection('normal search text')).to.equal(false); }); it('should handle edge cases', () => { expect(detectNoSQLInjection("")).to.equal(false); expect(detectNoSQLInjection(null)).to.equal(false); expect(detectNoSQLInjection(undefined)).to.equal(false); }); }); describe('XSS Detection', () => { it('should detect script injection attempts', () => { expect(detectXSSAttempts('<script>alert("xss")</script>')).to.equal(true); expect(detectXSSAttempts('<img src=x onerror=alert(1)>')).to.equal(true); expect(detectXSSAttempts('javascript:alert(document.cookie)')).to.equal(true); expect(detectXSSAttempts('<iframe src="javascript:alert()">')).to.equal(true); }); it('should detect event handler injection', () => { expect(detectXSSAttempts('onload="alert(1)"')).to.equal(true); expect(detectXSSAttempts('onclick="malicious()"')).to.equal(true); expect(detectXSSAttempts('onmouseover="steal()"')).to.equal(true); }); it('should allow safe HTML content', () => { expect(detectXSSAttempts('<p>Hello World</p>')).to.equal(false); expect(detectXSSAttempts('<div class="container">Content</div>')).to.equal(false); expect(detectXSSAttempts('Normal text content')).to.equal(false); expect(detectXSSAttempts('<img src="image.jpg" alt="photo">')).to.equal(false); }); it('should handle encoded payloads', () => { expect(detectXSSAttempts('%3Cscript%3Ealert%281%29%3C%2Fscript%3E')).to.equal(true); expect(detectXSSAttempts('&lt;script&gt;alert(1)&lt;/script&gt;')).to.equal(true); }); }); describe('Path Traversal Detection', () => { it('should detect directory traversal attempts', () => { expect(detectPathTraversal('../../../etc/passwd')).to.equal(true); expect(detectPathTraversal('..\\..\\windows\\system32')).to.equal(true); expect(detectPathTraversal('%2e%2e%2f%2e%2e%2fpasswd')).to.equal(true); expect(detectPathTraversal('....//....//etc/shadow')).to.equal(true); }); it('should allow safe file paths', () => { expect(detectPathTraversal('images/photo.jpg')).to.equal(false); expect(detectPathTraversal('documents/report.pdf')).to.equal(false); expect(detectPathTraversal('/api/users/123')).to.equal(false); }); it('should handle edge cases', () => { expect(detectPathTraversal("")).to.equal(false); expect(detectPathTraversal(null)).to.equal(false); expect(detectPathTraversal(undefined)).to.equal(false); }); }); describe('User Agent Analysis', () => { it('should detect suspicious automation tools', () => { const sqlmapResult = detectSuspiciousUserAgent('sqlmap/1.0'); expect(sqlmapResult.suspicious).to.equal(true); expect(sqlmapResult.pattern).to.equal('sqlmap'); const niktoResult = detectSuspiciousUserAgent('Nikto/2.1.6'); expect(niktoResult.suspicious).to.equal(true); expect(niktoResult.pattern).to.equal('nikto'); }); it('should detect scripting tools', () => { const curlResult = detectSuspiciousUserAgent('curl/7.68.0'); expect(curlResult.suspicious).to.equal(true); expect(curlResult.pattern).to.equal('curl'); const pythonResult = detectSuspiciousUserAgent('Python-urllib/3.8'); expect(pythonResult.suspicious).to.equal(true); expect(pythonResult.pattern).to.equal('python'); }); it('should allow legitimate browsers', () => { const chromeResult = detectSuspiciousUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'); expect(chromeResult.suspicious).to.equal(false); const firefoxResult = detectSuspiciousUserAgent('Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0'); expect(firefoxResult.suspicious).to.equal(false); }); it('should handle empty or malformed agents', () => { const emptyResult = detectSuspiciousUserAgent(''); expect(emptyResult.suspicious).to.equal(true); expect(emptyResult.pattern).to.equal('^$'); const nullResult = detectSuspiciousUserAgent(null); expect(nullResult.suspicious).to.equal(false); }); }); describe('Security Pattern Analysis', () => { it('should analyze comprehensive threat patterns', () => { const maliciousRequest = { body: { query: "'; DROP TABLE users; --" }, query: { search: '<script>alert(1)</script>' }, params: { file: '../../../etc/passwd' }, headers: { 'user-agent': 'sqlmap/1.0' } }; const analysis = analyzeSecurityPatterns(maliciousRequest); expect(analysis.threatDetected).to.equal(true); expect(analysis.detectionCount > 0).to.equal(true); }); it('should analyze safe requests', () => { const safeRequest = { body: { name: 'John Doe', email: 'john@example.com' }, query: { page: '1', limit: '10' }, params: { id: '12345' }, headers: { 'user-agent': 'Mozilla/5.0 (Chrome)' } }; const analysis = analyzeSecurityPatterns(safeRequest); expect(analysis.threatDetected).to.equal(false); expect(analysis.detectionCount).to.equal(0); }); it('should handle malformed requests', () => { const malformedRequest = {}; const analysis = analyzeSecurityPatterns(malformedRequest); expect(analysis.threatDetected).to.equal(false); expect(analysis.detectionCount).to.equal(0); }); }); describe('Security Event Logging', () => { it('should log security events with proper structure', () => { const eventData = { requestId: 'test-request-123', threatType: 'sql_injection', severity: 'high', sourceIP: '192.168.1.100', userAgent: 'sqlmap/1.0', detectionDetails: { pattern: 'DROP TABLE', location: 'body.query' } }; // Mock console.log to capture output const originalLog = console.log; let loggedData = null; console.log = (data) => { loggedData = data; }; logSecurityEvent(eventData); console.log = originalLog; expect(loggedData !== null).to.equal(true); expect(typeof loggedData === 'string').to.equal(true); expect(loggedData.includes('SECURITY ALERT')).to.equal(true); }); it('should handle missing event data gracefully', () => { const originalLog = console.log; let logCalled = false; console.log = () => { logCalled = true; }; logSecurityEvent({}); logSecurityEvent(null); console.log = originalLog; expect(logCalled).to.equal(true); }); }); describe('CSP Nonce Generation', () => { it('should generate secure nonces', () => { const nonce1 = generateSecureNonce(); const nonce2 = generateSecureNonce(); expect(typeof nonce1).to.equal('string'); expect(nonce1.length > 0).to.equal(true); expect(nonce1 !== nonce2).to.equal(true); // Should be unique }); it('should generate nonces with sufficient length', () => { const nonce = generateSecureNonce(); expect(nonce.length >= 16).to.equal(true); // Minimum secure length }); }); describe('CSP Header Builder', () => { it('should create CSP headers with nonce', () => { const nonce = 'test-nonce-123'; const cspHeader = createCSPHeader(nonce); expect(typeof cspHeader).to.equal('string'); expect(cspHeader.includes(`'nonce-${nonce}'`)).to.equal(true); expect(cspHeader.includes("default-src 'self'")).to.equal(true); }); it('should handle custom directives', () => { const nonce = 'test-nonce-456'; const customDirectives = { 'img-src': ["'self'", 'data:', 'https:'] }; const cspHeader = createCSPHeader(nonce, customDirectives); expect(cspHeader.includes("img-src 'self' data: https:")).to.equal(true); }); }); describe('CSP Nonce Validation', () => { it('should validate correct nonces', () => { const nonce = 'valid-nonce-123'; const isValid = validateCSPNonce(nonce, nonce); expect(isValid).to.equal(true); }); it('should reject invalid nonces', () => { const validNonce = 'valid-nonce-123'; const invalidNonce = 'invalid-nonce-456'; const isValid = validateCSPNonce(validNonce, invalidNonce); expect(isValid).to.equal(false); }); it('should handle empty nonces', () => { const isValid1 = validateCSPNonce('', 'some-nonce'); const isValid2 = validateCSPNonce('some-nonce', ''); expect(isValid1).to.equal(false); expect(isValid2).to.equal(false); }); }); describe('HTTPS Enforcement', () => { it('should create HTTPS middleware', () => { const middleware = enforceHTTPS(); expect(typeof middleware).to.equal('function'); expect(middleware.length).to.equal(3); // req, res, next }); it('should redirect HTTP to HTTPS', () => { const middleware = enforceHTTPS(); const mockReq = { header: (name) => name === 'x-forwarded-proto' ? 'http' : null, get: (name) => name === 'host' ? 'example.com' : null, url: '/api/test' }; const mockRes = { redirect: function(status, url) { this.redirectCalled = true; this.redirectArgs = [status, url]; } }; const mockNext = function() { this.called = true; }; middleware(mockReq, mockRes, mockNext); qtests.assert(mockRes.redirect.calledWith(301, 'https://example.com/api/test'), true); }); it('should allow HTTPS requests', () => { const middleware = enforceHTTPS(); const mockReq = { header: (name) => name === 'x-forwarded-proto' ? 'https' : null }; const mockRes = {}; const mockNext = sinon.stub(); middleware(mockReq, mockRes, mockNext); expect(mockNext.called).to.equal(true); }); }); describe('Security Middleware Factory', () => { it('should create security middleware with default options', () => { const middleware = createSecurityMiddleware(); expect(typeof middleware).to.equal('function'); }); it('should accept custom options', () => { const customOptions = { csp: { 'script-src': ["'self'", "'unsafe-inline'"] }, hsts: { maxAge: 7776000 } }; const middleware = createSecurityMiddleware(customOptions); expect(typeof middleware).to.equal('function'); }); }); }); module.exports = { runSecurityTests: () => expect().to.be.undefined };