Skip to main content
Glama
penetration-tests.test.ts22.6 kB
import request from 'supertest'; import express, { Application } from 'express'; import { authRouter } from '../../src/auth/routes'; import { mcpRouter } from '../../src/server/routes'; import { SecurityMiddleware } from '../../src/security/middleware'; import { MockFactory, TestDataGenerator, TestUtilities } from '../utils/test-helpers'; import { fixtures } from '../utils/fixtures'; // Mock dependencies for security testing jest.mock('../../src/config/config', () => ({ config: fixtures.config.testConfig, })); jest.mock('../../src/database/redis', () => ({ redis: MockFactory.createMockRedis(), })); jest.mock('../../src/database/prisma', () => ({ prisma: MockFactory.createMockPrisma(), })); jest.mock('../../src/utils/logger', () => ({ logger: MockFactory.createMockLogger(), })); describe('Security Penetration Tests', () => { let app: Application; let mockRedis: any; let mockPrisma: any; let mockLogger: any; beforeAll(() => { app = express(); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Apply security middleware app.use(SecurityMiddleware.checkSecurityHeaders); app.use(SecurityMiddleware.sanitizeRequestBody); app.use(SecurityMiddleware.detectSuspiciousActivity); app.use(SecurityMiddleware.honeypot); // Add routes app.use('/api/auth', authRouter); app.use('/api/mcp', mcpRouter); // Global error handler app.use((error: any, req: any, res: any, next: any) => { res.status(500).json({ error: 'Internal server error' }); }); // Setup mocks mockRedis = require('../../src/database/redis').redis; mockPrisma = require('../../src/database/prisma').prisma; mockLogger = require('../../src/utils/logger').logger; }); beforeEach(() => { jest.clearAllMocks(); }); describe('SQL Injection Attacks', () => { const sqlInjectionPayloads = fixtures.security.maliciousInputs.sqlInjection; it('should prevent SQL injection in login endpoint', async () => { for (const payload of sqlInjectionPayloads) { const response = await request(app) .post('/api/auth/login') .send({ email: payload, password: 'anypassword', }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } expect(mockLogger.warn).toHaveBeenCalledWith( expect.stringContaining('SQL injection attempt detected'), expect.any(Object) ); }); it('should prevent SQL injection in search parameters', async () => { const sqlPayload = "' UNION SELECT username, password FROM users --"; const response = await request(app) .get('/api/mcp/tools') .query({ search: sqlPayload }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); }); it('should prevent blind SQL injection attempts', async () => { const blindSqlPayloads = [ "' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' --", "' AND 1=1 --", "' AND 1=2 --", "' WAITFOR DELAY '00:00:05' --", ]; for (const payload of blindSqlPayloads) { const response = await request(app) .post('/api/auth/login') .send({ email: `admin${payload}@example.com`, password: 'password', }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } }); it('should prevent second-order SQL injection', async () => { const secondOrderPayload = "admin'; DROP TABLE sessions; --"; // First, attempt to inject via registration const registerResponse = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', username: secondOrderPayload, password: 'SecurePassword123!', firstName: 'Test', lastName: 'User', }); expect(registerResponse.status).toBe(400); expect(registerResponse.body.error).toBe('Invalid input detected'); }); }); describe('Cross-Site Scripting (XSS) Attacks', () => { const xssPayloads = fixtures.security.maliciousInputs.xssPayloads; it('should prevent reflected XSS attacks', async () => { for (const payload of xssPayloads) { const response = await request(app) .get('/api/mcp/resources') .query({ filter: payload }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } }); it('should prevent stored XSS attacks', async () => { for (const payload of xssPayloads) { const response = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', firstName: payload, lastName: 'User', password: 'SecurePassword123!', }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } }); it('should prevent DOM-based XSS in API responses', async () => { const maliciousInput = { userInput: '<img src=x onerror=alert("XSS")>', }; mockPrisma.user.findUnique.mockResolvedValue({ ...fixtures.users.validUser, bio: maliciousInput.userInput, }); const response = await request(app) .get('/api/auth/profile') .set('Authorization', 'Bearer valid-token'); if (response.status === 200) { // Response should be sanitized expect(response.body.user?.bio).not.toMatchXSSPattern(); } }); it('should prevent XSS in error messages', async () => { const xssInError = '<script>alert("XSS")</script>'; const response = await request(app) .post('/api/auth/login') .send({ email: xssInError, password: 'password', }); expect(response.status).toBe(400); expect(JSON.stringify(response.body)).not.toMatchXSSPattern(); }); }); describe('Command Injection Attacks', () => { const commandInjectionPayloads = fixtures.security.maliciousInputs.commandInjection; it('should prevent OS command injection', async () => { for (const payload of commandInjectionPayloads) { const response = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', firstName: 'Test', lastName: payload, password: 'SecurePassword123!', }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } expect(mockLogger.warn).toHaveBeenCalledWith( expect.stringContaining('command injection attempt detected'), expect.any(Object) ); }); it('should prevent code injection in eval-like functions', async () => { const codeInjectionPayloads = [ '__import__("os").system("rm -rf /")', 'require("child_process").exec("rm -rf /")', 'eval("process.exit()")', 'Function("return process")().exit()', ]; for (const payload of codeInjectionPayloads) { const response = await request(app) .post('/api/mcp/tools/call') .send({ name: 'calculate', arguments: { expression: payload }, }); // Should either reject input or safely handle without execution expect([400, 401, 403]).toContain(response.status); } }); }); describe('Path Traversal Attacks', () => { const pathTraversalPayloads = fixtures.security.maliciousInputs.pathTraversal; it('should prevent directory traversal attacks', async () => { for (const payload of pathTraversalPayloads) { const response = await request(app) .get('/api/mcp/resources') .query({ path: payload }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } expect(mockLogger.warn).toHaveBeenCalledWith( expect.stringContaining('path traversal attempt detected'), expect.any(Object) ); }); it('should prevent file inclusion attacks', async () => { const fileInclusionPayloads = [ '../../../../etc/passwd', '....//....//....//etc/passwd', 'php://filter/convert.base64-encode/resource=/etc/passwd', 'file:///etc/passwd', ]; for (const payload of fileInclusionPayloads) { const response = await request(app) .post('/api/mcp/tools/call') .send({ name: 'read_file', arguments: { path: payload }, }); expect([400, 401, 403, 404]).toContain(response.status); } }); }); describe('NoSQL Injection Attacks', () => { const noSqlPayloads = fixtures.security.maliciousInputs.noSqlInjection; it('should prevent NoSQL injection in query parameters', async () => { for (const payload of noSqlPayloads) { const response = await request(app) .post('/api/auth/login') .send({ email: 'admin@example.com', password: payload, }); expect(response.status).toBe(400); expect(response.body.error).toBe('Invalid input detected'); } }); it('should prevent MongoDB operator injection', async () => { const mongoPayloads = [ { $gt: '' }, { $ne: null }, { $regex: '.*' }, { $where: 'this.password.match(/.*/)' }, ]; for (const payload of mongoPayloads) { const response = await request(app) .post('/api/auth/login') .send({ email: 'admin@example.com', password: payload, }); expect(response.status).toBe(400); } }); }); describe('Authentication Bypass Attempts', () => { it('should prevent JWT token manipulation', async () => { const manipulatedTokens = [ 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.', // None algorithm 'Bearer ' + 'a'.repeat(2048), // Oversized token 'Bearer invalid.token.format', 'Bearer ' + JSON.stringify({ alg: 'none' }), // Direct JSON ]; for (const token of manipulatedTokens) { const response = await request(app) .get('/api/auth/profile') .set('Authorization', token); expect(response.status).toBe(401); } }); it('should prevent session fixation attacks', async () => { const fixedSessionId = 'attacker-controlled-session-id'; const response = await request(app) .post('/api/auth/login') .set('Cookie', `sessionId=${fixedSessionId}`) .send({ email: 'user@example.com', password: 'password', }); // Should not use the provided session ID if (response.status === 200) { expect(response.headers['set-cookie']).not.toContain(fixedSessionId); } }); it('should prevent privilege escalation', async () => { const userToken = TestDataGenerator.generateJWTPayload(); const adminClaims = { ...userToken, roles: ['admin'], permissions: ['*'], }; // Try to use modified token const maliciousToken = TestUtilities.createJWT(adminClaims); const response = await request(app) .get('/api/auth/profile') .set('Authorization', `Bearer ${maliciousToken}`); expect(response.status).toBe(401); }); }); describe('Rate Limiting and DoS Protection', () => { it('should enforce rate limiting on authentication endpoints', async () => { mockRedis.incr.mockResolvedValue(101); // Exceed rate limit mockRedis.ttl.mockResolvedValue(60); const response = await request(app) .post('/api/auth/login') .send({ email: 'user@example.com', password: 'password', }); expect(response.status).toBe(429); expect(response.headers['retry-after']).toBeDefined(); }); it('should prevent application layer DoS attacks', async () => { const massivePayload = { data: 'x'.repeat(11 * 1024 * 1024), // 11MB payload }; const response = await request(app) .post('/api/auth/register') .send(massivePayload); expect(response.status).toBe(413); }); it('should prevent slowloris-style attacks', async () => { const slowRequests = Array.from({ length: 100 }, (_, i) => request(app) .post('/api/auth/login') .send({ email: `user${i}@example.com`, password: 'password', }) .timeout(1000) // Short timeout ); const results = await Promise.allSettled(slowRequests); // Should handle gracefully without crashing const successful = results.filter(r => r.status === 'fulfilled').length; const failed = results.filter(r => r.status === 'rejected').length; expect(successful + failed).toBe(100); }); it('should prevent regex DoS (ReDoS) attacks', async () => { const redosPayloads = [ 'a'.repeat(10000) + '!', '(' + 'a'.repeat(1000) + ')*', 'a'.repeat(5000) + 'X', ]; for (const payload of redosPayloads) { const startTime = Date.now(); const response = await request(app) .post('/api/auth/register') .send({ email: `${payload}@example.com`, password: 'SecurePassword123!', firstName: 'Test', lastName: 'User', }); const duration = Date.now() - startTime; // Should not take excessive time to process expect(duration).toBeLessThan(5000); // 5 seconds max expect([400, 413, 429]).toContain(response.status); } }); }); describe('Input Validation Bypass Attempts', () => { it('should prevent null byte injection', async () => { const nullBytePayloads = [ 'admin@example.com\x00.jpg', 'user\x00admin', 'password\x00; DROP TABLE users;', ]; for (const payload of nullBytePayloads) { const response = await request(app) .post('/api/auth/login') .send({ email: payload, password: 'password', }); expect(response.status).toBe(400); } }); it('should prevent Unicode normalization attacks', async () => { const unicodePayloads = [ 'admin@example.com', // Normal 'аdmin@example.com', // Cyrillic 'а' instead of 'a' 'admin@ехаmple.com', // Mixed scripts 'ᵃdmin@example.com', // Superscript ]; const responses = await Promise.all( unicodePayloads.map(payload => request(app) .post('/api/auth/login') .send({ email: payload, password: 'password', }) ) ); // Should handle Unicode consistently responses.forEach(response => { expect([400, 401, 404]).toContain(response.status); }); }); it('should prevent content type confusion attacks', async () => { const confusionTests = [ { contentType: 'application/x-www-form-urlencoded', body: 'email=admin@example.com&password=password', }, { contentType: 'text/xml', body: '<email>admin@example.com</email>', }, { contentType: 'multipart/form-data', body: '{"email":"admin@example.com","password":"password"}', }, ]; for (const test of confusionTests) { const response = await request(app) .post('/api/auth/login') .set('Content-Type', test.contentType) .send(test.body); // Should handle gracefully expect([400, 415, 422]).toContain(response.status); } }); }); describe('HTTP Header Injection Attacks', () => { it('should prevent CRLF injection in headers', async () => { const crlfPayloads = fixtures.security.maliciousInputs.headerInjection; for (const payload of crlfPayloads) { const response = await request(app) .post('/api/auth/login') .set('X-Forwarded-For', payload) .send({ email: 'user@example.com', password: 'password', }); // Response headers should not contain injected content const responseHeaders = JSON.stringify(response.headers); expect(responseHeaders).not.toContain('X-Injected-Header'); expect(responseHeaders).not.toContain('Set-Cookie: evil=true'); } }); it('should prevent host header injection', async () => { const maliciousHosts = [ 'evil.com', 'example.com.evil.com', 'localhost:8080@evil.com', ]; for (const host of maliciousHosts) { const response = await request(app) .post('/api/auth/login') .set('Host', host) .send({ email: 'user@example.com', password: 'password', }); // Should reject or handle safely if (response.status === 200) { expect(response.body).not.toContain('evil.com'); } } }); }); describe('Business Logic Vulnerabilities', () => { it('should prevent race conditions in authentication', async () => { const concurrentLogins = Array.from({ length: 10 }, () => request(app) .post('/api/auth/login') .send({ email: 'user@example.com', password: 'password', }) ); const responses = await Promise.all(concurrentLogins); // All should have consistent behavior const statuses = responses.map(r => r.status); const uniqueStatuses = [...new Set(statuses)]; expect(uniqueStatuses.length).toBeLessThanOrEqual(2); // Should be consistent }); it('should prevent account enumeration', async () => { const validEmail = 'existing@example.com'; const invalidEmail = 'nonexistent@example.com'; mockPrisma.user.findUnique .mockResolvedValueOnce(fixtures.users.validUser) // Valid user .mockResolvedValueOnce(null); // Invalid user const [validResponse, invalidResponse] = await Promise.all([ request(app) .post('/api/auth/login') .send({ email: validEmail, password: 'wrongpassword' }), request(app) .post('/api/auth/login') .send({ email: invalidEmail, password: 'wrongpassword' }), ]); // Response times and messages should be similar expect(validResponse.body.error).toBe(invalidResponse.body.error); expect(validResponse.status).toBe(invalidResponse.status); }); it('should prevent timing attacks on authentication', async () => { const measurements: number[] = []; for (let i = 0; i < 10; i++) { const startTime = Date.now(); await request(app) .post('/api/auth/login') .send({ email: 'nonexistent@example.com', password: 'password', }); measurements.push(Date.now() - startTime); } // Check timing consistency (coefficient of variation should be low) const mean = measurements.reduce((a, b) => a + b) / measurements.length; const variance = measurements.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / measurements.length; const stdDev = Math.sqrt(variance); const coefficientOfVariation = stdDev / mean; expect(coefficientOfVariation).toBeLessThan(0.5); // Timing should be consistent }); }); describe('Honeypot and Bot Detection', () => { it('should detect and block bot traffic', async () => { const botUserAgents = fixtures.security.suspiciousUserAgents; for (const userAgent of botUserAgents) { const response = await request(app) .post('/api/auth/login') .set('User-Agent', userAgent) .send({ email: 'user@example.com', password: 'password', }); // Should be flagged as suspicious expect([400, 403, 429]).toContain(response.status); } }); it('should trigger honeypot for automated submissions', async () => { const response = await request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'SecurePassword123!', firstName: 'Test', lastName: 'User', honeypot: 'bot-filled-this-field', // Honeypot field }); expect(response.status).toBe(200); expect(response.body.success).toBe(true); // Fake success expect(mockLogger.warn).toHaveBeenCalledWith( 'Honeypot trap triggered', expect.objectContaining({ honeypotValue: 'bot-filled-this-field', }) ); // Should not actually create the user expect(mockPrisma.user.create).not.toHaveBeenCalled(); }); }); describe('Security Monitoring and Alerting', () => { it('should log security events for monitoring', async () => { const maliciousRequest = await request(app) .post('/api/auth/login') .send({ email: "'; DROP TABLE users; --", password: '<script>alert("xss")</script>', }); expect(mockLogger.warn).toHaveBeenCalledWith( expect.stringContaining('injection attempt detected'), expect.any(Object) ); // Should include context for incident response const logCall = mockLogger.warn.mock.calls[0]; expect(logCall[1]).toMatchObject({ input: expect.any(String), }); }); it('should maintain audit trail for security events', async () => { await request(app) .post('/api/auth/login') .set('X-Forwarded-For', '127.0.0.1\r\nX-Injected: malicious') .send({ email: 'user@example.com', password: 'password', }); // Should log with request context expect(mockLogger.warn).toHaveBeenCalledWith( expect.stringContaining('Suspicious'), expect.objectContaining({ ip: expect.any(String), userAgent: expect.any(String), }) ); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/perfecxion-ai/secure-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server