Skip to main content
Glama

n8n-MCP

by 88-888
config-validator-security.test.tsโ€ข13.6 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ConfigValidator } from '@/services/config-validator'; import type { ValidationResult, ValidationError, ValidationWarning } from '@/services/config-validator'; // Mock the database vi.mock('better-sqlite3'); describe('ConfigValidator - Security Validation', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('Credential security', () => { it('should perform security checks for hardcoded credentials', () => { const nodeType = 'nodes-base.test'; const config = { api_key: 'sk-1234567890abcdef', password: 'my-secret-password', token: 'hardcoded-token' }; const properties = [ { name: 'api_key', type: 'string' }, { name: 'password', type: 'string' }, { name: 'token', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.filter(w => w.type === 'security')).toHaveLength(3); expect(result.warnings.some(w => w.property === 'api_key')).toBe(true); expect(result.warnings.some(w => w.property === 'password')).toBe(true); expect(result.warnings.some(w => w.property === 'token')).toBe(true); }); it('should validate HTTP Request with authentication in API URLs', () => { const nodeType = 'nodes-base.httpRequest'; const config = { method: 'GET', url: 'https://api.github.com/user/repos', authentication: 'none' }; const properties = [ { name: 'method', type: 'options' }, { name: 'url', type: 'string' }, { name: 'authentication', type: 'options' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('API endpoints typically require authentication') )).toBe(true); }); }); describe('Code execution security', () => { it('should warn about security issues with eval/exec', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const userInput = items[0].json.code; const result = eval(userInput); return [{json: {result}}]; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('eval/exec which can be a security risk') )).toBe(true); }); it('should detect infinite loops', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` while (true) { console.log('infinite loop'); } return items; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Infinite loop detected') )).toBe(true); }); }); describe('Database security', () => { it('should validate database query security', () => { const nodeType = 'nodes-base.postgres'; const config = { query: 'DELETE FROM users;' // Missing WHERE clause }; const properties = [ { name: 'query', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('DELETE query without WHERE clause') )).toBe(true); }); it('should check for SQL injection vulnerabilities', () => { const nodeType = 'nodes-base.mysql'; const config = { query: 'SELECT * FROM users WHERE id = ${userId}' }; const properties = [ { name: 'query', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('SQL injection') )).toBe(true); }); // DROP TABLE warning not implemented in current validator it.skip('should warn about DROP TABLE operations', () => { const nodeType = 'nodes-base.postgres'; const config = { query: 'DROP TABLE IF EXISTS user_sessions;' }; const properties = [ { name: 'query', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('DROP TABLE is a destructive operation') )).toBe(true); }); // TRUNCATE warning not implemented in current validator it.skip('should warn about TRUNCATE operations', () => { const nodeType = 'nodes-base.mysql'; const config = { query: 'TRUNCATE TABLE audit_logs;' }; const properties = [ { name: 'query', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('TRUNCATE is a destructive operation') )).toBe(true); }); it('should check for unescaped user input in queries', () => { const nodeType = 'nodes-base.postgres'; const config = { query: `SELECT * FROM users WHERE name = '{{ $json.userName }}'` }; const properties = [ { name: 'query', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('vulnerable to SQL injection') )).toBe(true); }); }); describe('Network security', () => { // HTTP vs HTTPS warning not implemented in current validator it.skip('should warn about HTTP (non-HTTPS) API calls', () => { const nodeType = 'nodes-base.httpRequest'; const config = { method: 'POST', url: 'http://api.example.com/sensitive-data', sendBody: true }; const properties = [ { name: 'method', type: 'options' }, { name: 'url', type: 'string' }, { name: 'sendBody', type: 'boolean' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Consider using HTTPS') )).toBe(true); }); // Localhost URL warning not implemented in current validator it.skip('should validate localhost/internal URLs', () => { const nodeType = 'nodes-base.httpRequest'; const config = { method: 'GET', url: 'http://localhost:8080/admin' }; const properties = [ { name: 'method', type: 'options' }, { name: 'url', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Accessing localhost/internal URLs') )).toBe(true); }); // Sensitive data in URL warning not implemented in current validator it.skip('should check for sensitive data in URLs', () => { const nodeType = 'nodes-base.httpRequest'; const config = { method: 'GET', url: 'https://api.example.com/users?api_key=secret123&token=abc' }; const properties = [ { name: 'method', type: 'options' }, { name: 'url', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Sensitive data in URL') )).toBe(true); }); }); describe('File system security', () => { // File system operations warning not implemented in current validator it.skip('should warn about dangerous file operations', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const fs = require('fs'); fs.unlinkSync('/etc/passwd'); return items; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('File system operations') )).toBe(true); }); // Path traversal warning not implemented in current validator it.skip('should check for path traversal vulnerabilities', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const path = items[0].json.userPath; const file = fs.readFileSync('../../../' + path); return [{json: {content: file.toString()}}]; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Path traversal') )).toBe(true); }); }); describe('Crypto and sensitive operations', () => { it('should validate crypto module usage', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const uuid = crypto.randomUUID(); return [{json: {id: uuid}}]; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'invalid_value' && w.message.includes('Using crypto without require') )).toBe(true); }); // Weak crypto algorithm warning not implemented in current validator it.skip('should warn about weak crypto algorithms', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const crypto = require('crypto'); const hash = crypto.createHash('md5'); hash.update(data); return [{json: {hash: hash.digest('hex')}}]; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('MD5 is cryptographically weak') )).toBe(true); }); // Environment variable access warning not implemented in current validator it.skip('should check for environment variable access', () => { const nodeType = 'nodes-base.code'; const config = { language: 'javascript', jsCode: ` const apiKey = process.env.SECRET_API_KEY; const dbPassword = process.env.DATABASE_PASSWORD; return [{json: {configured: !!apiKey}}]; ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'jsCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('Accessing environment variables') )).toBe(true); }); }); describe('Python security', () => { it('should warn about exec/eval in Python', () => { const nodeType = 'nodes-base.code'; const config = { language: 'python', pythonCode: ` user_code = items[0]['json']['code'] result = exec(user_code) return [{"json": {"result": result}}] ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'pythonCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('eval/exec which can be a security risk') )).toBe(true); }); // os.system usage warning not implemented in current validator it.skip('should check for subprocess/os.system usage', () => { const nodeType = 'nodes-base.code'; const config = { language: 'python', pythonCode: ` import os command = items[0]['json']['command'] os.system(command) return [{"json": {"executed": True}}] ` }; const properties = [ { name: 'language', type: 'options' }, { name: 'pythonCode', type: 'string' } ]; const result = ConfigValidator.validate(nodeType, config, properties); expect(result.warnings.some(w => w.type === 'security' && w.message.includes('os.system() can execute arbitrary commands') )).toBe(true); }); }); });

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/88-888/n8n-mcp'

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