Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
gitleaks-detector.test.tsโ€ข11.8 kB
/** * Test suite for gitleaks-detector utility */ import { jest } from '@jest/globals'; // Mock child_process to control gitleaks behavior in tests const mockExecSync = jest.fn(); const mockExistsSync = jest.fn(); const mockReadFileSync = jest.fn(); const mockUnlinkSync = jest.fn(); const mockWriteFileSync = jest.fn(); // Set up mocks BEFORE importing the module jest.unstable_mockModule('child_process', () => ({ execSync: mockExecSync, spawn: jest.fn(), })); jest.unstable_mockModule('fs', () => ({ existsSync: mockExistsSync, readFileSync: mockReadFileSync, unlinkSync: mockUnlinkSync, writeFileSync: mockWriteFileSync, })); describe('Gitleaks Detector', () => { let analyzeSensitiveContent: any; let isObviouslySensitive: any; beforeAll(async () => { // Import the module after mocking const module = await import('../../src/utils/gitleaks-detector.js'); analyzeSensitiveContent = module.analyzeSensitiveContent; isObviouslySensitive = module.isObviouslySensitive; }); beforeEach(() => { jest.clearAllMocks(); // Mock writeFileSync to avoid actual file operations mockWriteFileSync.mockImplementation(() => {}); // Mock unlinkSync to avoid actual file deletion mockUnlinkSync.mockImplementation(() => {}); }); describe('analyzeSensitiveContent', () => { it('should return empty result when no secrets found', async () => { // Mock gitleaks returning success (no secrets) mockExecSync.mockImplementation(() => ''); // Mock existsSync for config file check mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; return false; }); const result = await analyzeSensitiveContent('test.js', 'console.log("hello");'); expect(result).toEqual({ filePath: 'test.js', hasIssues: false, matches: [], summary: { criticalCount: 0, highCount: 0, mediumCount: 0, lowCount: 0, totalCount: 0, }, recommendations: ['No sensitive content detected by gitleaks'], }); }); it('should parse gitleaks output when secrets are found', async () => { // Setup: Create proper error object with status 1 const gitleaksError = new Error('Command failed') as any; gitleaksError.status = 1; // Setup: Mock execSync to throw the error mockExecSync.mockImplementation(() => { throw gitleaksError; }); // Setup: Mock existsSync to return true for output files mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; if (path.includes('gitleaks-output')) return true; return false; }); // Setup: Mock the gitleaks JSON output const gitleaksOutput = [ { Description: 'GitHub Personal Access Token', StartLine: 1, EndLine: 1, StartColumn: 10, EndColumn: 50, Match: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', Secret: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', File: 'test.js', RuleID: 'github-pat', Tags: ['key', 'github'], Entropy: 5.2, Commit: '', SymlinkFile: '', Author: '', Email: '', Date: '', Message: '', Fingerprint: '', }, ]; mockReadFileSync.mockReturnValue(JSON.stringify(gitleaksOutput)); // Execute const result = await analyzeSensitiveContent( 'test.js', 'const token = "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";' ); // Verify expect(result.hasIssues).toBe(true); expect(result.matches).toHaveLength(1); expect(result.matches[0].pattern.name).toBe('github-pat'); expect(result.matches[0].pattern.severity).toBe('critical'); expect(result.summary.criticalCount).toBe(1); }); it('should handle gitleaks execution errors gracefully', async () => { // Mock gitleaks failing with real error (not status 1) const error = new Error('Gitleaks not found') as any; error.status = 127; mockExecSync.mockImplementation(() => { throw error; }); const result = await analyzeSensitiveContent('test.js', 'console.log("hello");'); expect(result.hasIssues).toBe(false); expect(result.matches).toHaveLength(0); }); it('should categorize rules correctly', async () => { const error = new Error('Gitleaks found secrets') as any; error.status = 1; mockExecSync.mockImplementation(() => { throw error; }); mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; if (path.includes('gitleaks-output')) return true; return false; }); const gitleaksOutput = [ { Description: 'Database password', StartLine: 1, EndLine: 1, StartColumn: 10, EndColumn: 30, Match: 'password123', Secret: 'password123', File: 'test.js', RuleID: 'db-password', Tags: ['password'], Entropy: 3.5, Commit: '', SymlinkFile: '', Author: '', Email: '', Date: '', Message: '', Fingerprint: '', }, ]; mockReadFileSync.mockReturnValue(JSON.stringify(gitleaksOutput)); const result = await analyzeSensitiveContent('test.js', 'const pass = "password123";'); expect(result.matches[0].pattern.category).toBe('secrets'); expect(result.matches[0].pattern.severity).toBe('high'); }); it('should clean up temporary files', async () => { mockExecSync.mockImplementation(() => ''); mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; // For cleanup, we need to simulate that temp files exist if (path.includes('gitleaks-scan-') || path.includes('gitleaks-output-')) return true; return false; }); await analyzeSensitiveContent('test.js', 'console.log("hello");'); expect(mockUnlinkSync).toHaveBeenCalled(); }); }); describe('isObviouslySensitive', () => { it('should identify sensitive file names', () => { expect(isObviouslySensitive('.env')).toBe(true); expect(isObviouslySensitive('.env.local')).toBe(true); expect(isObviouslySensitive('secrets.json')).toBe(true); expect(isObviouslySensitive('credentials.txt')).toBe(true); expect(isObviouslySensitive('private.key')).toBe(true); expect(isObviouslySensitive('id_rsa')).toBe(true); expect(isObviouslySensitive('cert.pem')).toBe(true); }); it('should not flag normal file names', () => { expect(isObviouslySensitive('README.md')).toBe(false); expect(isObviouslySensitive('package.json')).toBe(false); expect(isObviouslySensitive('src/index.js')).toBe(false); expect(isObviouslySensitive('test.ts')).toBe(false); }); }); describe('severity mapping', () => { it('should assign critical severity to important services', async () => { const testCases = [ { ruleId: 'github-pat', expected: 'critical' }, { ruleId: 'aws-access-key', expected: 'critical' }, { ruleId: 'stripe-secret', expected: 'critical' }, { ruleId: 'private-key', expected: 'critical' }, { ruleId: 'database-url', expected: 'critical' }, ]; for (const testCase of testCases) { const error = new Error('Gitleaks found secrets') as any; error.status = 1; mockExecSync.mockImplementation(() => { throw error; }); mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; if (path.includes('gitleaks-output')) return true; return false; }); const gitleaksOutput = [ { Description: 'Test secret', StartLine: 1, EndLine: 1, StartColumn: 1, EndColumn: 10, Match: 'secret', Secret: 'secret', File: 'test.js', RuleID: testCase.ruleId, Tags: [], Entropy: 4.0, Commit: '', SymlinkFile: '', Author: '', Email: '', Date: '', Message: '', Fingerprint: '', }, ]; mockReadFileSync.mockReturnValue(JSON.stringify(gitleaksOutput)); const result = await analyzeSensitiveContent('test.js', 'test content'); expect(result.matches[0].pattern.severity).toBe(testCase.expected); } }); }); describe('suggestions generation', () => { it('should provide appropriate suggestions for different secret types', async () => { const error = new Error('Gitleaks found secrets') as any; error.status = 1; mockExecSync.mockImplementation(() => { throw error; }); mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; if (path.includes('gitleaks-output')) return true; return false; }); const gitleaksOutput = [ { Description: 'API Key', StartLine: 1, EndLine: 1, StartColumn: 1, EndColumn: 10, Match: 'api_key_123', Secret: 'api_key_123', File: 'test.js', RuleID: 'api-key', Tags: ['key'], Entropy: 4.0, Commit: '', SymlinkFile: '', Author: '', Email: '', Date: '', Message: '', Fingerprint: '', }, ]; mockReadFileSync.mockReturnValue(JSON.stringify(gitleaksOutput)); const result = await analyzeSensitiveContent('test.js', 'const key = "api_key_123";'); expect(result.matches[0].suggestions).toContain('Move to environment variables'); expect(result.matches[0].suggestions).toContain('Use a secrets management service'); }); }); describe('recommendations', () => { it('should provide critical warnings for critical issues', async () => { const error = new Error('Gitleaks found secrets') as any; error.status = 1; mockExecSync.mockImplementation(() => { throw error; }); mockExistsSync.mockImplementation((path: string) => { if (path.includes('.gitleaks.toml')) return true; if (path.includes('gitleaks-output')) return true; return false; }); const gitleaksOutput = [ { Description: 'Private Key', StartLine: 1, EndLine: 1, StartColumn: 1, EndColumn: 10, Match: '-----BEGIN PRIVATE KEY-----', Secret: '-----BEGIN PRIVATE KEY-----', File: 'test.js', RuleID: 'private-key', Tags: ['key'], Entropy: 4.0, Commit: '', SymlinkFile: '', Author: '', Email: '', Date: '', Message: '', Fingerprint: '', }, ]; mockReadFileSync.mockReturnValue(JSON.stringify(gitleaksOutput)); const result = await analyzeSensitiveContent( 'test.js', 'const key = "-----BEGIN PRIVATE KEY-----";' ); expect(result.recommendations).toContain( '๐Ÿšจ 1 CRITICAL security issue(s) found - DO NOT COMMIT' ); expect(result.recommendations).toContain('Rotate any exposed credentials immediately'); }); }); });

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