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
JavaScript
/**
* 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('<script>alert(1)</script>')).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 };