Skip to main content
Glama
security.test.ts10.7 kB
import request from 'supertest'; import express from 'express'; import { configureCORS, configureSecurityHeaders, customSecurityMiddleware, securityLogger, securityValidation, applySecurityMiddleware, } from '../security'; // Mock console.warn at the module level const consoleWarnSpy = jest.fn(); beforeEach(() => { // Clear the spy and replace console.warn with our mock consoleWarnSpy.mockClear(); console.warn = consoleWarnSpy; }); describe('Security Middleware', () => { let app: express.Express; describe('CORS Configuration', () => { beforeEach(() => { app = express(); app.use(configureCORS()); app.get('/test', (req, res) => { res.json({ message: 'success' }); }); }); it('should handle preflight OPTIONS requests', async () => { const response = await request(app) .options('/test') .set('Origin', 'https://example.com') .set('Access-Control-Request-Method', 'GET'); expect(response.status).toBe(204); }); it('should allow GET requests with CORS headers', async () => { const response = await request(app) .get('/test') .set('Origin', 'https://example.com'); expect(response.headers['access-control-allow-origin']).toBe('https://example.com'); }); it('should expose rate limit headers', async () => { const response = await request(app) .get('/test') .set('Origin', 'https://example.com'); expect(response.headers['access-control-expose-headers']).toContain('X-RateLimit-Limit'); }); }); describe('CORS with Environment Configuration', () => { beforeEach(() => { process.env.CORS_ALLOWED_ORIGINS = 'https://app.example.com,https://admin.example.com'; app = express(); app.use(configureCORS()); app.get('/test', (req, res) => { res.json({ message: 'success' }); }); }); afterEach(() => { delete process.env.CORS_ALLOWED_ORIGINS; }); it('should allow requests from allowed origins', async () => { const response = await request(app) .get('/test') .set('Origin', 'https://app.example.com'); expect(response.headers['access-control-allow-origin']).toBe('https://app.example.com'); }); it('should reject requests from disallowed origins', async () => { const response = await request(app) .get('/test') .set('Origin', 'https://malicious.com'); expect(response.headers['access-control-allow-origin']).toBeUndefined(); }); }); describe('Security Headers', () => { beforeEach(() => { app = express(); app.use(configureSecurityHeaders()); app.get('/test', (req, res) => { res.json({ message: 'success' }); }); }); it('should set Content Security Policy header', async () => { const response = await request(app).get('/test'); expect(response.headers['content-security-policy']).toBeDefined(); }); it('should set HSTS header', async () => { const response = await request(app).get('/test'); expect(response.headers['strict-transport-security']).toBeDefined(); }); it('should set X-Content-Type-Options header', async () => { const response = await request(app).get('/test'); expect(response.headers['x-content-type-options']).toBe('nosniff'); }); it('should set X-Frame-Options header', async () => { const response = await request(app).get('/test'); expect(response.headers['x-frame-options']).toBe('DENY'); }); it('should remove X-Powered-By header', async () => { const response = await request(app).get('/test'); expect(response.headers['x-powered-by']).toBeUndefined(); }); it('should set Referrer-Policy header', async () => { const response = await request(app).get('/test'); expect(response.headers['referrer-policy']).toBe('no-referrer-when-downgrade'); }); }); describe('Custom Security Middleware', () => { beforeEach(() => { app = express(); app.use(customSecurityMiddleware); app.get('/test', (req, res) => res.json({ message: 'success' })); app.post('/api/data', (req, res) => res.json({ message: 'posted' })); app.post('/auth/login', (req, res) => res.json({ message: 'login' })); }); it('should set custom API version header', async () => { const response = await request(app).get('/test'); expect(response.headers['x-api-version']).toBe('1.0.0'); }); it('should set request ID header', async () => { const response = await request(app).get('/test'); expect(response.headers['x-request-id']).toBeDefined(); }); it('should set no-cache headers for non-GET API endpoints', async () => { const response = await request(app).post('/api/data'); expect(response.headers['cache-control']).toBe('no-store, no-cache, must-revalidate, private'); }); it('should add timing protection for auth endpoints', async () => { const start = Date.now(); await request(app).post('/auth/login'); const duration = Date.now() - start; expect(duration).toBeGreaterThanOrEqual(0); }); }); describe('Security Logger', () => { beforeEach(() => { app = express(); app.use(securityLogger); app.get('/test', (req, res) => { res.json({ message: 'success' }); }); }); it('should log security information for requests', async () => { const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); await request(app).get('/test'); expect(consoleLogSpy).toHaveBeenCalled(); consoleLogSpy.mockRestore(); }); }); describe('Security Validation', () => { beforeEach(() => { app = express(); app.use(express.json()); app.use(securityValidation); // Fallback for requests that pass validation app.use((req, res) => { if (!res.headersSent) { res.status(200).json({ message: 'Request passed validation' }); } }); }); it('should allow normal requests', async () => { const response = await request(app).get('/test'); expect(response.status).toBe(200); }); it('should block directory traversal attempts', async () => { // Use URL encoding to prevent normalization const response = await request(app).get('/test/..%2F..%2F..%2Fetc%2Fpasswd'); expect(response.status).toBe(400); expect(response.body.code).toBe('SECURITY_VIOLATION'); }); it('should block XSS attempts in URL', async () => { const response = await request(app).get('/test?<script>alert("xss")</script>'); expect(response.status).toBe(400); expect(response.body.code).toBe('SECURITY_VIOLATION'); }); it('should reject negative Content-Length', async () => { // Create a custom middleware to simulate negative Content-Length const testApp = express(); testApp.use(express.json()); // Custom middleware to set negative Content-Length testApp.use((req: express.Request, res: express.Response, next: express.NextFunction) => { req.headers['content-length'] = '-1'; next(); }); testApp.use(securityValidation); testApp.use((req: express.Request, res: express.Response) => { if (!res.headersSent) { res.status(200).json({ message: 'Request passed validation' }); } }); const response = await request(testApp).post('/upload').send({ data: 'test' }); expect(response.status).toBe(400); expect(response.body.code).toBe('INVALID_CONTENT_LENGTH'); }); it('should reject oversized payloads', async () => { const response = await request(app) .post('/upload') .send({ data: 'a'.repeat(2 * 1024 * 1024) }); // 2MB expect(response.status).toBe(413); }); it('should warn about invalid User-Agent headers', async () => { // Clear the spy before this test consoleWarnSpy.mockClear(); // Create a custom middleware to remove User-Agent header entirely const testApp = express(); testApp.use(express.json()); // Custom middleware to remove User-Agent header testApp.use((req: express.Request, res: express.Response, next: express.NextFunction) => { delete req.headers['user-agent']; next(); }); testApp.use(securityValidation); testApp.use((req: express.Request, res: express.Response) => { if (!res.headersSent) { res.status(200).json({ message: 'Request passed validation' }); } }); await request(testApp).get('/test'); // Debug: check all console.warn calls console.log('Console warn calls:', consoleWarnSpy.mock.calls); expect(consoleWarnSpy).toHaveBeenCalledWith('Security Warning: Missing or empty User-Agent header.'); }); it('should warn about oversized User-Agent headers', async () => { // Clear the spy before this test consoleWarnSpy.mockClear(); // Create a custom middleware to set oversized User-Agent header const testApp = express(); testApp.use(express.json()); // Custom middleware to set oversized User-Agent header testApp.use((req: express.Request, res: express.Response, next: express.NextFunction) => { req.headers['user-agent'] = 'a'.repeat(300); next(); }); testApp.use(securityValidation); testApp.use((req: express.Request, res: express.Response) => { if (!res.headersSent) { res.status(200).json({ message: 'Request passed validation' }); } }); await request(testApp).get('/test'); // Debug: check all console.warn calls console.log('Console warn calls:', consoleWarnSpy.mock.calls); expect(consoleWarnSpy).toHaveBeenCalledWith('Security Warning: Oversized User-Agent header: 300 chars'); }); }); describe('Complete Security Middleware Stack', () => { beforeEach(() => { app = express(); const middlewares = applySecurityMiddleware(); middlewares.forEach(middleware => app.use(middleware)); app.get('/test', (req, res) => { res.json({ message: 'success' }); }); }); it('should block malicious requests even with full stack', async () => { const response = await request(app).get('/test?evil=<script>alert(1)</script>'); expect(response.status).toBe(400); expect(response.body.code).toBe('SECURITY_VIOLATION'); }); }); });

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/learnwithcc/tally-mcp'

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