Skip to main content
Glama
SecurityUtils.test.ts6.96 kB
/** * Security Utils Unit Tests * Tests for DOI validation, query sanitization, injection prevention */ import { describe, it, expect, beforeEach } from '@jest/globals'; import { sanitizeDoi, escapeQueryValue, validateQueryComplexity, withTimeout, sanitizeRequest, maskSensitiveData } from '../../src/utils/SecurityUtils.js'; describe('SecurityUtils', () => { describe('sanitizeDoi', () => { it('should accept valid DOI formats', () => { const result = sanitizeDoi('10.1038/nature12373'); expect(result.valid).toBe(true); expect(result.sanitized).toBe('10.1038/nature12373'); }); it('should clean DOI URL prefixes', () => { const result1 = sanitizeDoi('https://doi.org/10.1038/nature12373'); expect(result1.valid).toBe(true); expect(result1.sanitized).toBe('10.1038/nature12373'); const result2 = sanitizeDoi('http://dx.doi.org/10.1038/nature12373'); expect(result2.valid).toBe(true); expect(result2.sanitized).toBe('10.1038/nature12373'); const result3 = sanitizeDoi('doi:10.1038/nature12373'); expect(result3.valid).toBe(true); expect(result3.sanitized).toBe('10.1038/nature12373'); }); it('should reject invalid DOI formats', () => { const result1 = sanitizeDoi('invalid-doi'); expect(result1.valid).toBe(false); const result2 = sanitizeDoi('11.1234/test'); // DOI must start with 10. expect(result2.valid).toBe(false); const result3 = sanitizeDoi(''); expect(result3.valid).toBe(false); }); it('should detect injection attempts', () => { const result1 = sanitizeDoi('10.1038/nature12373; DROP TABLE papers;'); expect(result1.valid).toBe(false); const result2 = sanitizeDoi('10.1038/nature12373<script>alert(1)</script>'); expect(result2.valid).toBe(false); }); it('should handle DOIs with special characters', () => { const result = sanitizeDoi('10.1000/xyz123'); expect(result.valid).toBe(true); }); }); describe('escapeQueryValue', () => { it('should escape special characters for WoS', () => { const result = escapeQueryValue('machine "learning"', 'wos'); expect(result).not.toContain('"'); }); it('should escape special characters for Springer', () => { const result = escapeQueryValue('test & query', 'springer'); expect(result).toBeDefined(); }); it('should handle empty strings', () => { const result = escapeQueryValue('', 'wos'); expect(result).toBe(''); }); it('should handle SQL injection patterns', () => { const result = escapeQueryValue("'; DROP TABLE --", 'wos'); // escapeQueryValue sanitizes but may not remove all characters expect(result).toBeDefined(); }); }); describe('validateQueryComplexity', () => { it('should accept simple queries', () => { const result = validateQueryComplexity('machine learning'); expect(result.valid).toBe(true); }); it('should accept queries with reasonable boolean operators', () => { const result = validateQueryComplexity('machine AND learning OR deep', { maxBooleanOperators: 5 }); expect(result.valid).toBe(true); }); it('should reject queries exceeding max length', () => { const longQuery = 'a'.repeat(1001); const result = validateQueryComplexity(longQuery, { maxLength: 1000 }); expect(result.valid).toBe(false); expect(result.error).toBeDefined(); }); it('should reject queries with too many boolean operators', () => { const query = 'a AND b AND c AND d AND e AND f AND g AND h AND i AND j AND k'; const result = validateQueryComplexity(query, { maxBooleanOperators: 5 }); expect(result.valid).toBe(false); expect(result.error).toContain('boolean'); }); it('should detect potential injection patterns', () => { const result1 = validateQueryComplexity('SELECT * FROM papers'); // Depending on implementation, this may or may not be valid expect(result1).toBeDefined(); }); }); describe('withTimeout', () => { it('should resolve before timeout', async () => { const promise = Promise.resolve('success'); const result = await withTimeout(promise, 1000); expect(result).toBe('success'); }); it('should reject on timeout', async () => { const slowPromise = new Promise(resolve => setTimeout(resolve, 2000)); await expect(withTimeout(slowPromise, 100, 'Timeout!')).rejects.toThrow('Timeout!'); }); it('should propagate errors from original promise', async () => { const failingPromise = Promise.reject(new Error('Original error')); await expect(withTimeout(failingPromise, 1000)).rejects.toThrow('Original error'); }); }); describe('sanitizeRequest', () => { it('should mask API keys in headers', () => { const config = { headers: { 'X-ApiKey': 'secret-key-12345', 'Content-Type': 'application/json' } }; const sanitized = sanitizeRequest(config); expect(sanitized.headers['X-ApiKey']).not.toBe('secret-key-12345'); expect(sanitized.headers['X-ApiKey']).toContain('***'); expect(sanitized.headers['Content-Type']).toBe('application/json'); }); it('should mask Authorization headers', () => { const config = { headers: { 'Authorization': 'Bearer token123' } }; const sanitized = sanitizeRequest(config); expect(sanitized.headers['Authorization']).toContain('***'); }); it('should mask api_key in params', () => { const config = { params: { api_key: 'secret123', query: 'machine learning' } }; const sanitized = sanitizeRequest(config); expect(sanitized.params.api_key).toContain('***'); expect(sanitized.params.query).toBe('machine learning'); }); it('should handle null/undefined config', () => { // sanitizeRequest returns the input as-is for null/undefined expect(sanitizeRequest(null)).toBeNull(); expect(sanitizeRequest(undefined)).toBeUndefined(); }); }); describe('maskSensitiveData', () => { it('should mask API keys in strings', () => { const input = 'Error with api_key=secret123'; const result = maskSensitiveData(input); expect(result).not.toContain('secret123'); }); it('should mask Bearer tokens', () => { const input = 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'; const result = maskSensitiveData(input); expect(result).not.toContain('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'); }); it('should process all strings', () => { const input = 'Normal error message'; const result = maskSensitiveData(input); // maskSensitiveData may apply masking patterns expect(typeof result).toBe('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/Dianel555/paper-search-mcp-nodejs'

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