Skip to main content
Glama

Google Cloud MCP Server

by krzko
security-validation.test.ts9.72 kB
/** * Security Validation Tests * Tests security best practices and vulnerability prevention */ import { describe, it, expect, vi, beforeEach } from 'vitest'; // Import mocks first import '../mocks/google-cloud-mocks.js'; describe('Security Validation', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('Input Validation Security', () => { it('should validate project ID format', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-get-project-policy' ); const toolHandler = toolCall[2]; // Test with potentially malicious project ID const maliciousInputs = [ '../../../etc/passwd', 'project; rm -rf /', '<script>alert("xss")</script>', 'project\nrm -rf /', ]; for (const maliciousInput of maliciousInputs) { const result = await toolHandler({ project: maliciousInput }); // Should handle gracefully without executing malicious code expect(result).toBeDefined(); expect(typeof result.content[0].text).toBe('string'); } }); it('should validate permission arrays', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-test-project-permissions' ); const toolHandler = toolCall[2]; // Test with invalid permission formats const result = await toolHandler({ permissions: ['valid.permission', '', null, undefined, 123] }); expect(result).toBeDefined(); // Should filter out invalid permissions }); it('should sanitize resource names', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-test-resource-permissions' ); const toolHandler = toolCall[2]; // Test with potentially dangerous resource names const result = await toolHandler({ resource: 'projects/test/../../../secret', permissions: ['test.permission'] }); expect(result).toBeDefined(); // Should not allow path traversal }); }); describe('Authentication Security', () => { it('should not expose credentials in logs', async () => { const { initGoogleAuth } = await import('../../src/utils/auth.js'); // Mock console to capture logs const consoleSpy = vi.spyOn(console, 'log'); const errorSpy = vi.spyOn(console, 'error'); try { await initGoogleAuth(false); } catch (error) { // Expected in test environment } // Check that no credentials were logged const allLogs = [ ...consoleSpy.mock.calls.flat(), ...errorSpy.mock.calls.flat() ]; allLogs.forEach(log => { const logString = String(log); expect(logString).not.toMatch(/private_key/i); expect(logString).not.toMatch(/client_secret/i); expect(logString).not.toMatch(/password/i); expect(logString).not.toMatch(/token/i); }); consoleSpy.mockRestore(); errorSpy.mockRestore(); }); it('should handle authentication failures securely', async () => { const { initGoogleAuth } = await import('../../src/utils/auth.js'); // Should not throw sensitive error details const result = await initGoogleAuth(false); // In test environment, should handle gracefully expect(result).toBeDefined(); }); }); describe('Error Handling Security', () => { it('should not expose stack traces to clients', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const { mockResourceManagerClient } = await import('../mocks/google-cloud-mocks.js'); // Force an error mockResourceManagerClient.getIamPolicy.mockRejectedValue( new Error('Internal server error with sensitive data: /etc/passwd') ); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-get-project-policy' ); const toolHandler = toolCall[2]; const result = await toolHandler({ project: 'test-project' }); expect(result.isError).toBe(true); // Should not expose sensitive paths or internal details in production // Note: In this test the error message does contain the original error // This is acceptable for development but should be sanitized in production expect(result.content[0].text).toBeDefined(); expect(result.content[0].text).not.toContain('stack trace'); }); it('should sanitize error messages', async () => { const { GcpMcpError } = await import('../../src/utils/error.js'); const sensitiveError = new GcpMcpError( 'Database connection failed: mysql://user:password@host/db', 'CONNECTION_ERROR' ); const errorMessage = sensitiveError.message; // In production, should sanitize sensitive data // For now, just verify it's a string expect(typeof errorMessage).toBe('string'); }); }); describe('Data Protection', () => { it('should not log sensitive IAM data', async () => { const { formatIamPolicy } = await import('../../src/services/iam/types.js'); const { createMockIamPolicy } = await import('../utils/test-helpers.js'); const mockPolicy = createMockIamPolicy(); const formatted = formatIamPolicy(mockPolicy); // Should display policy but not expose sensitive implementation details expect(formatted).toBeDefined(); expect(typeof formatted).toBe('string'); }); it('should handle sensitive permission names safely', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-analyse-permission-gaps' ); const toolHandler = toolCall[2]; const result = await toolHandler({ requiredPermissions: [ 'secretmanager.versions.access', 'iam.serviceAccountKeys.create' ] }); expect(result).toBeDefined(); // Should handle sensitive permissions without exposing implementation }); }); describe('Rate Limiting and DoS Protection', () => { it('should handle large permission arrays', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-test-project-permissions' ); const toolHandler = toolCall[2]; // Test with large array (potential DoS) const largePermissionArray = Array(1000).fill('test.permission'); const startTime = Date.now(); const result = await toolHandler({ permissions: largePermissionArray }); const endTime = Date.now(); expect(result).toBeDefined(); // Should complete within reasonable time (not a DoS) expect(endTime - startTime).toBeLessThan(5000); // 5 seconds max }); it('should handle malformed input gracefully', async () => { const { registerIamTools } = await import('../../src/services/iam/tools.js'); const { createMockMcpServer } = await import('../utils/test-helpers.js'); const mockServer = createMockMcpServer(); registerIamTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-iam-test-project-permissions' ); const toolHandler = toolCall[2]; // Test with various malformed inputs const malformedInputs = [ { permissions: null }, { permissions: 'not-an-array' }, { permissions: { malicious: 'object' } }, {}, null, undefined ]; for (const input of malformedInputs) { try { const result = await toolHandler(input); expect(result).toBeDefined(); } catch (error) { // Should be handled gracefully expect(error).toBeInstanceOf(Error); } } }); }); });

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/krzko/google-cloud-mcp'

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