const axios = require('axios'); const BASE_URL = 'http://localhost:3000/api'; const DOCS_URL = 'http://localhost:3000/api-docs'; // Colors for terminal output const colors = { reset: '\x1b[0m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m' }; let testsPassed = 0; let testsFailed = 0; /** * Test helper functions */ const log = (message, color = 'reset') => { console.log(`${colors[color]}${message}${colors.reset}`); }; const testResult = (testName, passed, details = '') => { if (passed) { testsPassed++; log(`āœ… ${testName}`, 'green'); if (details) log(` ${details}`, 'cyan'); } else { testsFailed++; log(`āŒ ${testName}`, 'red'); if (details) log(` ${details}`, 'yellow'); } }; const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); /** * Test 1: Security Headers (Helmet) */ async function testSecurityHeaders() { log('\nšŸ“‹ Test 1: Security Headers', 'blue'); try { const response = await axios.get(`${BASE_URL}/categories`); const headers = response.headers; // Check for essential security headers const hasXContentTypeOptions = headers['x-content-type-options'] === 'nosniff'; const hasXFrameOptions = headers['x-frame-options'] === 'DENY'; const hasXXssProtection = headers['x-xss-protection'] === '1; mode=block' || !headers['x-xss-protection']; // Optional (deprecated) const hasStrictTransportSecurity = headers['strict-transport-security']?.includes('max-age'); const noPoweredBy = !headers['x-powered-by']; testResult( 'Security headers present', hasXContentTypeOptions && hasXFrameOptions && hasStrictTransportSecurity && noPoweredBy, `X-Content-Type: ${hasXContentTypeOptions}, X-Frame: ${hasXFrameOptions}, HSTS: ${hasStrictTransportSecurity}, No X-Powered-By: ${noPoweredBy}` ); } catch (error) { testResult('Security headers present', false, error.message); } } /** * Test 2: Rate Limiting - General API */ async function testApiRateLimit() { log('\nšŸ“‹ Test 2: API Rate Limiting (100 req/15min)', 'blue'); try { // Make multiple requests to test rate limiting const requests = []; for (let i = 0; i < 5; i++) { requests.push(axios.get(`${BASE_URL}/categories`)); } const responses = await Promise.all(requests); const firstResponse = responses[0]; // Check for rate limit headers const hasRateLimitHeaders = firstResponse.headers['ratelimit-limit'] && firstResponse.headers['ratelimit-remaining'] !== undefined; testResult( 'API rate limit headers present', hasRateLimitHeaders, `Limit: ${firstResponse.headers['ratelimit-limit']}, Remaining: ${firstResponse.headers['ratelimit-remaining']}` ); } catch (error) { testResult('API rate limit headers present', false, error.message); } } /** * Test 3: Rate Limiting - Login Endpoint */ async function testLoginRateLimit() { log('\nšŸ“‹ Test 3: Login Rate Limiting (5 req/15min)', 'blue'); try { // Attempt multiple login requests const requests = []; for (let i = 0; i < 6; i++) { requests.push( axios.post(`${BASE_URL}/auth/login`, { email: 'test@example.com', password: 'wrongpassword' }).catch(err => err.response) ); await delay(100); // Small delay between requests } const responses = await Promise.all(requests); const rateLimited = responses.some(r => r && r.status === 429); testResult( 'Login rate limit enforced', rateLimited, rateLimited ? 'Rate limit triggered after multiple attempts' : 'May need more requests to trigger' ); } catch (error) { testResult('Login rate limit enforced', false, error.message); } } /** * Test 4: NoSQL Injection Protection */ async function testNoSQLInjection() { log('\nšŸ“‹ Test 4: NoSQL Injection Protection', 'blue'); try { // Attempt NoSQL injection in login const response = await axios.post(`${BASE_URL}/auth/login`, { email: { $gt: '' }, password: { $gt: '' } }).catch(err => err.response); // Should either get 400 validation error or sanitized input (not 200) const protected = response.status !== 200; testResult( 'NoSQL injection prevented', protected, `Status: ${response.status} - ${response.data.message || 'Input sanitized'}` ); } catch (error) { testResult('NoSQL injection prevented', false, error.message); } } /** * Test 5: XSS Protection */ async function testXSSProtection() { log('\nšŸ“‹ Test 5: XSS Protection', 'blue'); try { // Attempt XSS in registration const xssPayload = ''; const response = await axios.post(`${BASE_URL}/auth/register`, { username: xssPayload, email: 'xss@test.com', password: 'Password123!' }).catch(err => err.response); // Should either reject or sanitize const responseData = JSON.stringify(response.data); const sanitized = !responseData.includes('