← back to Handbag Auth Nextjs
auction-viewer/test-security.js
331 lines
#!/usr/bin/env node
const http = require('http');
const https = require('https');
const HOST = '45.61.58.125';
const PORT = 7500;
const BASE_URL = `http://${HOST}:${PORT}`;
// Color codes for console 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 function
async function testEndpoint(name, options, expectedStatus = 200, expectFail = false) {
return new Promise((resolve) => {
const url = new URL(options.path || '/', BASE_URL);
const reqOptions = {
hostname: HOST,
port: PORT,
path: url.pathname + url.search,
method: options.method || 'GET',
headers: options.headers || {},
timeout: 5000
};
const req = http.request(reqOptions, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const success = expectFail
? res.statusCode !== expectedStatus
: res.statusCode === expectedStatus;
if (success) {
console.log(`${colors.green}✓${colors.reset} ${name}`);
testsPassed++;
} else {
console.log(`${colors.red}✗${colors.reset} ${name} - Expected ${expectedStatus}, got ${res.statusCode}`);
testsFailed++;
}
// Check security headers
if (res.headers['x-content-type-options']) {
console.log(` ${colors.cyan}→ X-Content-Type-Options: ${res.headers['x-content-type-options']}${colors.reset}`);
}
if (res.headers['strict-transport-security']) {
console.log(` ${colors.cyan}→ HSTS: Present${colors.reset}`);
}
if (res.headers['x-frame-options']) {
console.log(` ${colors.cyan}→ X-Frame-Options: ${res.headers['x-frame-options']}${colors.reset}`);
}
resolve({ status: res.statusCode, headers: res.headers, data });
});
});
req.on('error', (err) => {
console.log(`${colors.red}✗${colors.reset} ${name} - ${err.message}`);
testsFailed++;
resolve({ error: err.message });
});
req.on('timeout', () => {
req.destroy();
console.log(`${colors.red}✗${colors.reset} ${name} - Timeout`);
testsFailed++;
resolve({ error: 'Timeout' });
});
if (options.body) {
req.write(options.body);
}
req.end();
});
}
// Test SQL Injection Prevention
async function testSQLInjection() {
console.log(`\n${colors.blue}Testing SQL Injection Prevention:${colors.reset}`);
await testEndpoint(
'SQL Injection in sort parameter',
{ path: '/api/best-values?sort=percent; DROP TABLE auctions;--' },
400,
false
);
await testEndpoint(
'SQL Injection in limit parameter',
{ path: '/api/auctions?limit=1 OR 1=1' },
400,
false
);
await testEndpoint(
'Valid sort parameter',
{ path: '/api/best-values?sort=percent' },
200
);
}
// Test Path Traversal Prevention
async function testPathTraversal() {
console.log(`\n${colors.blue}Testing Path Traversal Prevention:${colors.reset}`);
await testEndpoint(
'Path traversal attempt in static files',
{ path: '/../../../etc/passwd' },
404
);
await testEndpoint(
'Path traversal with encoded dots',
{ path: '/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd' },
404
);
}
// Test Rate Limiting
async function testRateLimiting() {
console.log(`\n${colors.blue}Testing Rate Limiting:${colors.reset}`);
// Make multiple rapid requests
const promises = [];
for (let i = 0; i < 25; i++) {
promises.push(
testEndpoint(
`Rate limit request ${i + 1}`,
{ path: '/api/logs' },
i < 20 ? 200 : 429,
false
)
);
}
await Promise.all(promises);
}
// Test Input Validation
async function testInputValidation() {
console.log(`\n${colors.blue}Testing Input Validation:${colors.reset}`);
await testEndpoint(
'Invalid limit parameter (negative)',
{ path: '/api/auctions?limit=-1' },
400
);
await testEndpoint(
'Invalid limit parameter (too large)',
{ path: '/api/auctions?limit=10000' },
400
);
await testEndpoint(
'Invalid sort parameter',
{ path: '/api/best-values?sort=invalid' },
400
);
await testEndpoint(
'Valid parameters',
{ path: '/api/auctions?limit=10&offset=0' },
200
);
}
// Test Security Headers
async function testSecurityHeaders() {
console.log(`\n${colors.blue}Testing Security Headers:${colors.reset}`);
const result = await testEndpoint(
'Security headers on main page',
{ path: '/' },
200
);
if (result.headers) {
const requiredHeaders = [
'x-content-type-options',
'x-frame-options',
'x-xss-protection',
'strict-transport-security',
'content-security-policy'
];
requiredHeaders.forEach(header => {
if (result.headers[header]) {
console.log(`${colors.green}✓${colors.reset} Header ${header} is present`);
testsPassed++;
} else {
console.log(`${colors.yellow}⚠${colors.reset} Header ${header} is missing`);
}
});
}
}
// Test CORS Restrictions
async function testCORS() {
console.log(`\n${colors.blue}Testing CORS Restrictions:${colors.reset}`);
await testEndpoint(
'CORS with invalid origin',
{
path: '/api/auctions',
headers: { 'Origin': 'http://evil-site.com' }
},
500,
false
);
await testEndpoint(
'CORS with valid origin',
{
path: '/api/auctions',
headers: { 'Origin': `http://${HOST}:${PORT}` }
},
200
);
}
// Test Error Handling
async function testErrorHandling() {
console.log(`\n${colors.blue}Testing Error Handling:${colors.reset}`);
const result = await testEndpoint(
'Non-existent endpoint',
{ path: '/api/nonexistent' },
404
);
if (result.data) {
try {
const json = JSON.parse(result.data);
if (json.error && !json.error.includes('stack')) {
console.log(`${colors.green}✓${colors.reset} Error messages don't expose stack traces`);
testsPassed++;
}
} catch (e) {
// Invalid JSON
}
}
}
// Test File Size Limits
async function testFileSizeLimits() {
console.log(`\n${colors.blue}Testing File Size Limits:${colors.reset}`);
// Test large body rejection
const largeBody = 'x'.repeat(11 * 1024 * 1024); // 11MB
await testEndpoint(
'Large request body rejection',
{
method: 'POST',
path: '/api/auctions',
headers: {
'Content-Type': 'application/json',
'Content-Length': largeBody.length
},
body: largeBody
},
413,
false
);
}
// Test Health Endpoints
async function testHealthEndpoints() {
console.log(`\n${colors.blue}Testing Health Endpoints:${colors.reset}`);
await testEndpoint(
'Health check endpoint',
{ path: '/health' },
200
);
await testEndpoint(
'Status endpoint',
{ path: '/api/status' },
200
);
}
// Main test runner
async function runSecurityTests() {
console.log(`
╔═══════════════════════════════════════════════════════╗
║ LUXVAULT SECURITY TEST SUITE ║
╠═══════════════════════════════════════════════════════╣
║ Testing: http://${HOST}:${PORT} ║
╚═══════════════════════════════════════════════════════╝
`);
await testSQLInjection();
await testPathTraversal();
await testInputValidation();
await testSecurityHeaders();
await testCORS();
await testErrorHandling();
await testHealthEndpoints();
await testRateLimiting(); // Run rate limiting last
// Summary
console.log(`
╔═══════════════════════════════════════════════════════╗
║ TEST SUMMARY ║
╠═══════════════════════════════════════════════════════╣
║ ${colors.green}Passed: ${testsPassed}${colors.reset} ║
║ ${colors.red}Failed: ${testsFailed}${colors.reset} ║
║ Total: ${testsPassed + testsFailed} ║
╚═══════════════════════════════════════════════════════╝
`);
process.exit(testsFailed > 0 ? 1 : 0);
}
// Run tests
runSecurityTests().catch(console.error);