← back to Handbag Auth Nextjs
auction-viewer/security-test.js
327 lines
#!/usr/bin/env node
/**
* Security Test Suite for LUXVAULT Auction API
* Tests various security measures and vulnerabilities
*/
const axios = require('axios');
const colors = require('colors');
const BASE_URL = 'http://45.61.58.125:7500';
class SecurityTester {
constructor() {
this.results = [];
this.passed = 0;
this.failed = 0;
}
async test(name, fn) {
process.stdout.write(`Testing ${name}... `);
try {
await fn();
console.log('✅ PASSED'.green);
this.passed++;
this.results.push({ test: name, status: 'PASSED' });
} catch (error) {
console.log(`❌ FAILED: ${error.message}`.red);
this.failed++;
this.results.push({ test: name, status: 'FAILED', error: error.message });
}
}
async runTests() {
console.log('='.repeat(60));
console.log('LUXVAULT Security Test Suite'.bold);
console.log('='.repeat(60));
console.log();
// Test 1: SQL Injection Protection
await this.test('SQL Injection Protection', async () => {
const maliciousInputs = [
"'; DROP TABLE auctions; --",
"1 OR 1=1",
"' UNION SELECT * FROM users --",
"'; DELETE FROM auctions WHERE '1'='1"
];
for (const input of maliciousInputs) {
const response = await axios.get(`${BASE_URL}/api/auctions`, {
params: { search: input },
validateStatus: () => true
});
if (response.status === 500 || !response.data.success) {
// Good - the injection was blocked
continue;
}
// Check if data is still intact
if (response.data.data && response.data.data.length > 0) {
// Data exists, injection didn't work
continue;
}
throw new Error(`SQL injection not blocked for: ${input}`);
}
});
// Test 2: XSS Protection
await this.test('XSS Protection Headers', async () => {
const response = await axios.get(`${BASE_URL}/api/health`, {
validateStatus: () => true
});
const headers = response.headers;
if (!headers['x-content-type-options']) {
throw new Error('Missing X-Content-Type-Options header');
}
if (!headers['x-frame-options']) {
throw new Error('Missing X-Frame-Options header');
}
if (!headers['x-xss-protection']) {
throw new Error('Missing X-XSS-Protection header');
}
if (!headers['content-security-policy']) {
throw new Error('Missing Content-Security-Policy header');
}
});
// Test 3: Rate Limiting
await this.test('Rate Limiting', async () => {
const requests = [];
const limit = 110; // Should exceed the 100 request limit
for (let i = 0; i < limit; i++) {
requests.push(
axios.get(`${BASE_URL}/api/health`, {
validateStatus: () => true
})
);
}
const responses = await Promise.all(requests);
const rateLimited = responses.some(r => r.status === 429);
if (!rateLimited) {
throw new Error('Rate limiting not working');
}
});
// Test 4: Input Validation
await this.test('Input Validation', async () => {
const invalidInputs = [
{ limit: -1 },
{ limit: 10000 },
{ offset: -100 },
{ sort: 'invalid_column' }
];
for (const input of invalidInputs) {
const response = await axios.get(`${BASE_URL}/api/auctions`, {
params: input,
validateStatus: () => true
});
if (response.status === 200 && response.data.success) {
// Check if the input was properly validated
if (input.limit && (response.data.data.length > 1000 || response.data.data.length < 0)) {
throw new Error(`Invalid limit not validated: ${input.limit}`);
}
}
}
});
// Test 5: Path Traversal Protection
await this.test('Path Traversal Protection', async () => {
const maliciousPaths = [
'../../../etc/passwd',
'..\\..\\..\\windows\\system32\\config\\sam',
'%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
];
for (const path of maliciousPaths) {
try {
const response = await axios.get(`${BASE_URL}/api/logs`, {
params: { file: path },
validateStatus: () => true,
headers: {
'Authorization': 'Bearer test_token'
}
});
if (response.status === 200) {
// Check if sensitive data was exposed
const data = JSON.stringify(response.data);
if (data.includes('root:') || data.includes('Administrator')) {
throw new Error(`Path traversal vulnerability with: ${path}`);
}
}
} catch (error) {
// Network error is fine, means the request was blocked
}
}
});
// Test 6: CORS Configuration
await this.test('CORS Configuration', async () => {
const response = await axios.options(`${BASE_URL}/api/health`, {
headers: {
'Origin': 'http://evil.com',
'Access-Control-Request-Method': 'GET'
},
validateStatus: () => true
});
const allowOrigin = response.headers['access-control-allow-origin'];
if (allowOrigin === '*' || allowOrigin === 'http://evil.com') {
throw new Error('CORS is too permissive');
}
});
// Test 7: Information Disclosure
await this.test('Information Disclosure', async () => {
// Try to trigger an error
const response = await axios.get(`${BASE_URL}/api/auctions`, {
params: { limit: 'not_a_number' },
validateStatus: () => true
});
if (response.status === 500 || response.status === 400) {
const errorMessage = JSON.stringify(response.data);
// Check for sensitive information
if (errorMessage.includes('stack') ||
errorMessage.includes('node_modules') ||
errorMessage.includes('/root/') ||
errorMessage.includes('SQL') ||
errorMessage.includes('Database')) {
throw new Error('Sensitive information disclosed in error message');
}
}
});
// Test 8: Authentication on Sensitive Endpoints
await this.test('Authentication Required for Logs', async () => {
const response = await axios.get(`${BASE_URL}/api/logs`, {
validateStatus: () => true
});
if (response.status === 200) {
throw new Error('Logs endpoint accessible without authentication');
}
if (response.status !== 401) {
throw new Error(`Expected 401, got ${response.status}`);
}
});
// Test 9: Request Size Limits
await this.test('Request Size Limits', async () => {
const largePayload = 'x'.repeat(2 * 1024 * 1024); // 2MB
try {
const response = await axios.post(`${BASE_URL}/api/test`, {
data: largePayload
}, {
validateStatus: () => true,
maxContentLength: Infinity,
maxBodyLength: Infinity
});
if (response.status === 200) {
throw new Error('Large payload accepted');
}
} catch (error) {
// Error is expected for oversized payload
}
});
// Test 10: HTTP Security Headers
await this.test('HSTS Header Present', async () => {
const response = await axios.get(`${BASE_URL}/api/health`, {
validateStatus: () => true
});
const hsts = response.headers['strict-transport-security'];
if (!hsts) {
throw new Error('HSTS header missing');
}
if (!hsts.includes('max-age=31536000')) {
throw new Error('HSTS max-age too short');
}
});
// Print summary
console.log();
console.log('='.repeat(60));
console.log('Test Results Summary'.bold);
console.log('='.repeat(60));
console.log(`✅ Passed: ${this.passed}`.green);
console.log(`❌ Failed: ${this.failed}`.red);
console.log(`📊 Total: ${this.passed + this.failed}`);
console.log();
// Security score
const score = Math.round((this.passed / (this.passed + this.failed)) * 100);
let rating;
let color;
if (score >= 90) {
rating = 'EXCELLENT';
color = 'green';
} else if (score >= 70) {
rating = 'GOOD';
color = 'yellow';
} else if (score >= 50) {
rating = 'FAIR';
color = 'orange';
} else {
rating = 'POOR';
color = 'red';
}
console.log(`Security Score: ${score}% - ${rating}`[color].bold);
console.log();
// Detailed results
if (this.failed > 0) {
console.log('Failed Tests:'.red.bold);
this.results
.filter(r => r.status === 'FAILED')
.forEach(r => {
console.log(` ❌ ${r.test}: ${r.error}`);
});
}
return score;
}
}
// Run tests
async function main() {
const tester = new SecurityTester();
try {
const score = await tester.runTests();
process.exit(score >= 70 ? 0 : 1);
} catch (error) {
console.error('Test suite error:', error);
process.exit(1);
}
}
// Check if colors module is installed
try {
require('colors');
} catch (e) {
console.log('Installing required module: colors');
require('child_process').execSync('npm install colors', { stdio: 'inherit' });
}
main();