Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
content-masking-tool.test.ts29 kB
/** * Unit Tests for content-masking-tool.ts * Target Coverage: 18.6% → 80% * * Following methodological pragmatism principles: * - Systematic Verification: Structured test cases with clear assertions * - Explicit Fallibilism: Testing both success and failure scenarios * - Pragmatic Success Criteria: Focus on reliable, maintainable test coverage * - Cognitive Systematization: Organized test structure covering all exported functions */ import { jest } from '@jest/globals'; import { McpAdrError } from '../../src/types/index.js'; // Mock config to provide test-safe paths jest.mock('../../src/utils/config.js', () => ({ loadConfig: jest.fn(() => ({ projectPath: '/tmp/test-project', adrDirectory: '/tmp/test-project/docs/adrs', logLevel: 'INFO', cacheEnabled: true, cacheDirectory: '.mcp-adr-cache', maxCacheSize: 100 * 1024 * 1024, analysisTimeout: 30000, })), })); // Mock enhanced logging to prevent actual logging jest.mock('../../src/utils/enhanced-logging.js', () => ({ EnhancedLogger: jest.fn().mockImplementation(() => ({ info: jest.fn(), debug: jest.fn(), warn: jest.fn(), error: jest.fn(), })), })); // Mock filesystem operations for memory system const mockFsPromises = { access: jest.fn().mockResolvedValue(undefined), mkdir: jest.fn().mockResolvedValue(undefined), readFile: jest.fn().mockRejectedValue(new Error('ENOENT')), writeFile: jest.fn().mockResolvedValue(undefined), }; jest.mock('fs', () => ({ promises: mockFsPromises, })); // Mock crypto for consistent UUIDs jest.mock('crypto', () => ({ randomUUID: jest.fn(() => 'test-uuid-123'), })); import { analyzeContentSecurity, generateContentMasking, configureCustomPatterns, applyBasicContentMasking, validateContentMasking, } from '../../src/tools/content-masking-tool.js'; describe('Content Masking Tool', () => { beforeEach(() => { jest.clearAllMocks(); // Reset filesystem mocks mockFsPromises.access.mockResolvedValue(undefined); mockFsPromises.mkdir.mockResolvedValue(undefined); mockFsPromises.readFile.mockRejectedValue(new Error('ENOENT')); mockFsPromises.writeFile.mockResolvedValue(undefined); // Mock process.cwd() for consistent test environment - use /tmp instead of Users path jest.spyOn(process, 'cwd').mockReturnValue('/tmp/test-workspace/mcp-adr-analysis-server'); }); afterEach(() => { jest.restoreAllMocks(); }); // ============================================================================ // analyzeContentSecurity Function Tests // ============================================================================ describe('analyzeContentSecurity function', () => { describe('basic functionality', () => { test('analyzes content with default parameters', async () => { const result = await analyzeContentSecurity({ content: 'const apiKey = "sk-1234567890abcdef";', }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); expect(Array.isArray(result.content)).toBe(true); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toContain('Sensitive'); }); test('handles different content types', async () => { const testCases = ['code', 'documentation', 'configuration', 'logs', 'general'] as const; for (const contentType of testCases) { const result = await analyzeContentSecurity({ content: 'password=secret123', contentType, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('content'); } }); test('processes user-defined patterns', async () => { const result = await analyzeContentSecurity({ content: 'custom-secret-pattern-123', userDefinedPatterns: ['custom-secret-pattern-\\d+'], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('patterns'); }); }); describe('enhancement modes', () => { test('works with knowledge enhancement enabled', async () => { const result = await analyzeContentSecurity({ content: 'API_TOKEN=abc123', knowledgeEnhancement: true, enhancedMode: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Enhancement'); expect(result.content[0].text).toContain('Enabled'); }); test('works with enhancements disabled', async () => { const result = await analyzeContentSecurity({ content: 'API_TOKEN=abc123', knowledgeEnhancement: false, enhancedMode: false, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Disabled'); }); }); describe('memory integration (prompt-only mode)', () => { test('provides comprehensive analysis prompts when memory integration is enabled', async () => { const result = await analyzeContentSecurity({ content: 'password=secret123 api_key=sk-abcd1234', contentType: 'code', enableMemoryIntegration: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Sensitive Content Analysis'); expect(result.content[0].text).toContain('Content to Analyze'); expect(result.content[0].text).toContain('password=secret123 api_key=sk-abcd1234'); }); test('includes proper analysis instructions', async () => { const result = await analyzeContentSecurity({ content: 'const apiKey = "sk-1234567890abcdef"; const password = "secret123";', contentType: 'code', enableMemoryIntegration: true, userDefinedPatterns: ['api.*key', 'password'], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Detection Guidelines'); expect(result.content[0].text).toContain('Masking Strategies'); expect(result.content[0].text).toContain('JSON structure'); }); test('handles different content types correctly', async () => { const contentTypes = ['code', 'documentation', 'configuration', 'logs', 'general'] as const; for (const contentType of contentTypes) { const result = await analyzeContentSecurity({ content: 'password=secret123', contentType, enableMemoryIntegration: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Content Type'); expect(result.content[0].text).toContain(contentType); } }); test('includes user-defined patterns in analysis prompt', async () => { const result = await analyzeContentSecurity({ content: 'test content', contentType: 'code', enableMemoryIntegration: true, userDefinedPatterns: ['password=\\w+', 'api_key=\\w+', 'token=\\w+'], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('User-Defined Sensitive Patterns'); expect(result.content[0].text).toContain('password=\\w+'); expect(result.content[0].text).toContain('api_key=\\w+'); expect(result.content[0].text).toContain('token=\\w+'); }); }); describe('parameter validation', () => { test('throws error for empty content', async () => { await expect( analyzeContentSecurity({ content: '', }) ).rejects.toThrow(McpAdrError); await expect( analyzeContentSecurity({ content: ' ', }) ).rejects.toThrow(McpAdrError); }); test('throws error for missing content', async () => { await expect(analyzeContentSecurity({} as any)).rejects.toThrow(McpAdrError); }); }); describe('error handling', () => { test('handles analysis errors gracefully', async () => { // Test with extremely large content that might cause issues const largeContent = 'x'.repeat(100000); const result = await analyzeContentSecurity({ content: largeContent, }); expect(result).toBeDefined(); }); }); }); // ============================================================================ // generateContentMasking Function Tests // ============================================================================ describe('generateContentMasking function', () => { const sampleDetectedItems = [ { type: 'api_key', category: 'secret', content: 'sk-1234567890abcdef', startPosition: 10, endPosition: 30, confidence: 0.95, reasoning: 'Matches API key pattern', severity: 'high' as const, suggestedMask: '[API_KEY_REDACTED]', }, { type: 'password', category: 'credential', content: 'secret123', startPosition: 50, endPosition: 59, confidence: 0.8, reasoning: 'Password field detected', severity: 'medium' as const, suggestedMask: '[PASSWORD_REDACTED]', }, ]; describe('basic functionality', () => { test('generates masking for detected items', async () => { const result = await generateContentMasking({ content: 'const apiKey = "sk-1234567890abcdef"; const password = "secret123";', detectedItems: sampleDetectedItems, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); expect(result.content[0].text).toContain('Instructions'); }); test('handles different masking strategies', async () => { const strategies = ['full', 'partial', 'placeholder', 'environment'] as const; for (const strategy of strategies) { const result = await generateContentMasking({ content: 'sensitive content here', detectedItems: sampleDetectedItems, maskingStrategy: strategy, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); } }); test('handles no detected items', async () => { const result = await generateContentMasking({ content: 'no sensitive content here', detectedItems: [], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('No sensitive items detected'); }); }); describe('parameter validation', () => { test('throws error for empty content', async () => { await expect( generateContentMasking({ content: '', detectedItems: sampleDetectedItems, }) ).rejects.toThrow(McpAdrError); }); test('throws error for missing content', async () => { await expect( generateContentMasking({ detectedItems: sampleDetectedItems, } as any) ).rejects.toThrow(McpAdrError); }); }); describe('detected items processing', () => { test('handles items with missing optional fields', async () => { const minimalItems = [ { type: 'secret', content: 'sensitive', startPosition: 0, endPosition: 9, severity: 'high' as const, }, ]; const result = await generateContentMasking({ content: 'sensitive data', detectedItems: minimalItems, }); expect(result).toBeDefined(); }); test('processes items with all fields', async () => { const result = await generateContentMasking({ content: 'test content', detectedItems: sampleDetectedItems, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Items'); }); }); describe('memory integration features', () => { test('works with memory integration enabled', async () => { const result = await generateContentMasking({ content: 'const apiKey = "sk-1234567890abcdef"; const password = "secret123";', detectedItems: sampleDetectedItems, enableMemoryIntegration: true, contentType: 'code', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); }); test('works with memory integration disabled', async () => { const result = await generateContentMasking({ content: 'test content', detectedItems: sampleDetectedItems, enableMemoryIntegration: false, contentType: 'general', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); }); test('handles memory integration with various content types', async () => { const contentTypes = ['code', 'documentation', 'configuration', 'logs', 'general'] as const; for (const contentType of contentTypes) { const result = await generateContentMasking({ content: 'password=secret123', detectedItems: sampleDetectedItems, enableMemoryIntegration: true, contentType, }); expect(result).toBeDefined(); } }); }); }); // ============================================================================ // configureCustomPatterns Function Tests // ============================================================================ describe('configureCustomPatterns function', () => { describe('basic functionality', () => { test('configures patterns for project path', async () => { const result = await configureCustomPatterns({ projectPath: process.cwd(), }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Custom Pattern Configuration'); expect(result.content[0].text).toContain('AI Configuration Prompt'); }); test('includes existing patterns in analysis', async () => { const existingPatterns = ['api-key-\\w+', 'password=\\S+']; const result = await configureCustomPatterns({ projectPath: process.cwd(), existingPatterns, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Patterns'); }); }); describe('project structure analysis', () => { test('analyzes project structure correctly', async () => { const result = await configureCustomPatterns({ projectPath: process.cwd(), }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Project Structure'); expect(result.content[0].text).toContain('Root Path'); expect(result.content[0].text).toContain('Total Files'); }); }); describe('error handling', () => { test('handles invalid project paths gracefully', async () => { const result = await configureCustomPatterns({ projectPath: '/nonexistent/path', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Pattern Configuration'); expect(result.content[0].text).toContain('Total Files**: 0'); }); }); }); // ============================================================================ // applyBasicContentMasking Function Tests // ============================================================================ describe('applyBasicContentMasking function', () => { describe('basic functionality', () => { test('applies basic masking with default strategy', async () => { const result = await applyBasicContentMasking({ content: 'password=secret123 api_key=sk-abcd1234', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); expect(result.content[0].text).toContain('Content'); }); test('handles different masking strategies', async () => { const strategies = ['full', 'partial', 'placeholder'] as const; for (const strategy of strategies) { const result = await applyBasicContentMasking({ content: 'sensitive content', maskingStrategy: strategy, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Masking'); } }); }); describe('validation integration', () => { test('includes validation results', async () => { const result = await applyBasicContentMasking({ content: 'test content with secrets', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Validation'); expect(result.content[0].text).toContain('Score'); }); }); describe('parameter validation', () => { test('throws error for empty content', async () => { await expect( applyBasicContentMasking({ content: '', }) ).rejects.toThrow(McpAdrError); }); test('throws error for missing content', async () => { await expect(applyBasicContentMasking({} as any)).rejects.toThrow(McpAdrError); }); }); }); // ============================================================================ // validateContentMasking Function Tests // ============================================================================ describe('validateContentMasking function', () => { describe('basic functionality', () => { test('validates masking correctly', async () => { const originalContent = 'password=secret123 api_key=sk-abcd1234'; const maskedContent = 'password=[REDACTED] api_key=[REDACTED]'; const result = await validateContentMasking({ originalContent, maskedContent, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Content Masking Validation'); expect(result.content[0].text).toContain('Security Score'); expect(result.content[0].text).toContain('Is Valid'); }); test('calculates size changes correctly', async () => { const originalContent = 'short'; const maskedContent = 'much longer masked content'; const result = await validateContentMasking({ originalContent, maskedContent, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Size Change'); expect(result.content[0].text).toContain('%'); }); }); describe('security assessment', () => { test('provides security assessment based on score', async () => { const result = await validateContentMasking({ originalContent: 'test content', maskedContent: 'masked content', }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Security Assessment'); // Should contain one of the security level indicators expect(result.content[0].text).toMatch(/🟢|🟡|🟠|🔴/); }); }); describe('parameter validation', () => { test('throws error for missing original content', async () => { await expect( validateContentMasking({ originalContent: '', maskedContent: 'masked', }) ).rejects.toThrow(McpAdrError); }); test('throws error for missing masked content', async () => { await expect( validateContentMasking({ originalContent: 'original', maskedContent: '', }) ).rejects.toThrow(McpAdrError); }); test('throws error for missing parameters', async () => { await expect(validateContentMasking({} as any)).rejects.toThrow(McpAdrError); }); }); }); // ============================================================================ // Integration and Edge Case Tests // ============================================================================ describe('Integration and Edge Cases', () => { describe('full workflow integration', () => { test('handles complete security analysis to masking workflow', async () => { const content = 'const apiKey = "sk-1234567890abcdef"; const password = "secret123";'; // Step 1: Analyze content security const analysisResult = await analyzeContentSecurity({ content, contentType: 'code', }); expect(analysisResult).toBeDefined(); // Step 2: Apply basic masking (simulating detected items) const maskingResult = await applyBasicContentMasking({ content, maskingStrategy: 'full', }); expect(maskingResult).toBeDefined(); // Step 3: Validate the masking (using mock masked content) const validationResult = await validateContentMasking({ originalContent: content, maskedContent: 'const apiKey = "[REDACTED]"; const password = "[REDACTED]";', }); expect(validationResult).toBeDefined(); }); }); describe('performance and edge cases', () => { test('handles large content efficiently', async () => { const largeContent = 'sensitive data '.repeat(1000); const result = await analyzeContentSecurity({ content: largeContent, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Analysis'); }); test('handles special characters and encoding', async () => { const specialContent = 'password="spéciål_chårs_123" 🔑 api_key="test"'; const result = await analyzeContentSecurity({ content: specialContent, }); expect(result).toBeDefined(); }); test('handles empty detected items gracefully', async () => { const result = await generateContentMasking({ content: 'clean content with no secrets', detectedItems: [], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('No sensitive items detected'); }); }); describe('error recovery and resilience', () => { test('recovers from knowledge generation failures', async () => { // This test verifies the tool continues working even if knowledge enhancement fails const result = await analyzeContentSecurity({ content: 'test content', knowledgeEnhancement: true, enhancedMode: true, }); expect(result).toBeDefined(); // Should still provide analysis even if knowledge generation has issues }); }); }); // ============================================================================ // SecurityMemoryManager Class Tests (Internal functionality) // ============================================================================ describe('Advanced functionality and analysis patterns', () => { describe('comprehensive security pattern detection', () => { test('provides detailed analysis prompts for complex patterns', async () => { const result = await analyzeContentSecurity({ content: 'password=secret123 api_key=sk-abcd1234 token=xyz789', contentType: 'code', enableMemoryIntegration: true, userDefinedPatterns: ['password=\\w+', 'api_key=\\w+', 'token=\\w+'], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('User-Defined Sensitive Patterns'); expect(result.content[0].text).toContain('password=\\w+'); expect(result.content[0].text).toContain('api_key=\\w+'); expect(result.content[0].text).toContain('token=\\w+'); }); test('handles complex configuration files with sensitive data', async () => { const complexContent = ` const config = { database: { host: 'localhost', password: 'super_secret_password_123', apiKey: 'sk-1234567890abcdefghijklmnopqrstuvwxyz', token: 'jwt_token_abcdef123456' }, redis: { password: 'redis_secret_789', url: 'redis://user:pass@localhost:6379' } }; `; const result = await analyzeContentSecurity({ content: complexContent, contentType: 'configuration', enableMemoryIntegration: true, userDefinedPatterns: [ 'password["\']?\\s*:\\s*["\']\\w+["\']', 'apiKey["\']?\\s*:\\s*["\']sk-\\w+["\']', 'token["\']?\\s*:\\s*["\']\\w+["\']', ], }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('User-Defined Sensitive Patterns'); expect(result.content[0].text).toContain('Content Type'); expect(result.content[0].text).toContain('configuration'); }); test('integrates knowledge enhancement with security analysis', async () => { const result = await analyzeContentSecurity({ content: 'SECRET_KEY=prod_secret_key DATABASE_URL=postgres://user:pass@host:5432/db', contentType: 'configuration', enableMemoryIntegration: true, knowledgeEnhancement: true, enhancedMode: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('**Generated Knowledge Prompting**: ✅ Applied'); expect(result.content[0].text).toContain('**Enhanced Mode**: ✅ Applied'); expect(result.content[0].text).toContain('Security Knowledge Context'); }); }); describe('comprehensive workflow testing', () => { test('supports full security analysis workflow', async () => { // Multiple analysis calls to simulate real-world usage const contents = [ 'password=secret123 api_key=sk-abcd1234', 'const token = "jwt_eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9";', 'DATABASE_URL=postgres://user:pass@localhost:5432/db', ]; for (const content of contents) { const result = await analyzeContentSecurity({ content, contentType: 'code', enableMemoryIntegration: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Sensitive Content Analysis'); } }); test('handles complete masking workflow integration', async () => { const content = 'const secret = "very_secret_password"; const key = "api_key_12345";'; // Step 1: Analysis const analysisResult = await analyzeContentSecurity({ content, contentType: 'code', enableMemoryIntegration: true, }); expect(analysisResult).toBeDefined(); expect(analysisResult.content[0].text).toContain('Content to Analyze'); // Step 2: Generate masking instructions const maskingResult = await generateContentMasking({ content, detectedItems: [ { type: 'password', content: 'very_secret_password', startPosition: 16, endPosition: 36, severity: 'high' as const, }, ], enableMemoryIntegration: true, contentType: 'code', }); expect(maskingResult).toBeDefined(); expect(maskingResult.content[0].text).toContain('Masking'); }); }); describe('performance and resilience testing', () => { test('handles large content volumes efficiently', async () => { // Test with substantial content that might stress the system const largeContent = 'password=secret '.repeat(100) + 'api_key=sk-test '.repeat(100); const result = await analyzeContentSecurity({ content: largeContent, contentType: 'general', enableMemoryIntegration: true, }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Sensitive Information Detection Guide'); }); test('supports concurrent analysis operations', async () => { const promises = []; // Create multiple concurrent analysis requests for (let i = 0; i < 5; i++) { promises.push( analyzeContentSecurity({ content: `password=secret${i} api_key=sk-key${i}`, contentType: 'code', enableMemoryIntegration: true, }) ); } const results = await Promise.all(promises); for (const result of results) { expect(result).toBeDefined(); expect(result.content[0].text).toContain('Sensitive Content Analysis'); } }); }); }); });

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/tosin2013/mcp-adr-analysis-server'

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