Skip to main content
Glama
SecurityValidator.ts8.44 kB
import WorkflowValidator from '../resources/WorkflowValidator.js'; /** * Validateur pour les aspects de sécurité dans les workflows n8n */ class SecurityValidator implements WorkflowValidator { /** * Valide les aspects de sécurité dans un workflow n8n * @param workflow Les données du workflow à valider * @param strictness Le niveau de rigueur de la validation * @returns Résultat de la validation avec les problèmes détectés */ validate(workflow: any, strictness: 'low' | 'medium' | 'high') { const issues: Array<{ message: string; recommendation: string; severity: 'low' | 'medium' | 'high'; location?: string; }> = []; // Vérifier si le workflow est partagé publiquement if (workflow.active && workflow.settings?.callerPolicy === 'any') { issues.push({ message: 'Le workflow est actif et accessible publiquement', recommendation: 'Limitez l\'accès au workflow en configurant une politique d\'appelant restrictive', severity: 'high', }); } // Vérifier les informations sensibles en clair dans les nœuds if (workflow.nodes && Array.isArray(workflow.nodes)) { workflow.nodes.forEach((node: any) => { // Vérifier les informations d'identification en clair this.checkForSensitiveData(node, issues); // Vérifier les nœuds HTTP pour les problèmes de sécurité if ((node.type || '').toLowerCase().includes('http')) { this.checkHttpNodeSecurity(node, issues, strictness); } // Vérifier les nœuds d'exécution de code if (this.isCodeExecutionNode(node)) { this.checkCodeExecutionSecurity(node, issues, strictness); } }); } // Vérifier les webhooks pour l'authentification this.checkWebhookSecurity(workflow, issues, strictness); // Vérifier si le workflow utilise des variables d'environnement pour les secrets this.checkEnvironmentVariableUsage(workflow, issues, strictness); return { valid: issues.length === 0, issues, }; } /** * Vérifie si un nœud contient des informations sensibles en clair */ private checkForSensitiveData(node: any, issues: any[]): void { // Patterns pour détecter les informations sensibles const sensitivePatterns = [ { pattern: /password\s*[:=]\s*['"](?!{{)([^'"{}]*)['"]/, type: 'mot de passe' }, { pattern: /api[_\s]*key\s*[:=]\s*['"](?!{{)([^'"{}]*)['"]/, type: 'clé API' }, { pattern: /token\s*[:=]\s*['"](?!{{)([^'"{}]*)['"]/, type: 'token' }, { pattern: /secret\s*[:=]\s*['"](?!{{)([^'"{}]*)['"]/, type: 'secret' }, { pattern: /auth\s*[:=]\s*['"](?!{{)([^'"{}]*)['"]/, type: 'authentification' }, ]; // Convertir le nœud en chaîne pour la recherche const nodeString = JSON.stringify(node); // Vérifier chaque pattern sensitivePatterns.forEach(({ pattern, type }) => { if (pattern.test(nodeString)) { issues.push({ message: `Le nœud "${node.name}" contient un ${type} en clair`, recommendation: `Utilisez des variables d'environnement ou des credentials n8n pour stocker les ${type}s de manière sécurisée`, severity: 'high', location: node.id, }); } }); } /** * Vérifie la sécurité des nœuds HTTP */ private checkHttpNodeSecurity(node: any, issues: any[], strictness: string): void { // Vérifier si le nœud HTTP utilise HTTPS if (node.parameters?.url && typeof node.parameters.url === 'string' && node.parameters.url.startsWith('http:') && !node.parameters.url.includes('localhost')) { issues.push({ message: `Le nœud "${node.name}" utilise HTTP non sécurisé`, recommendation: 'Utilisez HTTPS pour toutes les requêtes externes', severity: strictness === 'low' ? 'medium' : 'high', location: node.id, }); } // Vérifier si le nœud HTTP vérifie les certificats SSL if (node.parameters?.allowUnauthorizedCerts === true) { issues.push({ message: `Le nœud "${node.name}" autorise les certificats SSL non vérifiés`, recommendation: 'Activez la vérification des certificats SSL pour éviter les attaques de type man-in-the-middle', severity: 'high', location: node.id, }); } } /** * Vérifie si un nœud est de type exécution de code */ private isCodeExecutionNode(node: any): boolean { const type = (node.type || '').toLowerCase(); return type.includes('code') || type.includes('script') || type.includes('function') || type.includes('execute') || type.includes('eval'); } /** * Vérifie la sécurité des nœuds d'exécution de code */ private checkCodeExecutionSecurity(node: any, issues: any[], strictness: string): void { // Vérifier si le code contient des imports potentiellement dangereux const dangerousImports = [ 'child_process', 'fs', 'path', 'os', 'crypto', 'net', 'dns', 'http', 'https' ]; const code = node.parameters?.code || node.parameters?.jsCode || ''; if (typeof code === 'string') { dangerousImports.forEach(importName => { if (code.includes(`require('${importName}')`) || code.includes(`require("${importName}")`)) { issues.push({ message: `Le nœud "${node.name}" utilise le module système potentiellement dangereux '${importName}'`, recommendation: 'Évitez d\'utiliser des modules système dans les nœuds de code pour réduire les risques de sécurité', severity: strictness === 'low' ? 'medium' : 'high', location: node.id, }); } }); // Vérifier l'utilisation d'eval ou de new Function if (code.includes('eval(') || code.includes('new Function(')) { issues.push({ message: `Le nœud "${node.name}" utilise eval() ou new Function() qui sont des pratiques dangereuses`, recommendation: 'Évitez d\'utiliser eval() ou new Function() car ils peuvent exécuter du code arbitraire', severity: 'high', location: node.id, }); } } } /** * Vérifie la sécurité des webhooks */ private checkWebhookSecurity(workflow: any, issues: any[], strictness: string): void { if (!workflow.nodes || !Array.isArray(workflow.nodes)) { return; } const webhookNodes = workflow.nodes.filter((node: any) => (node.type || '').toLowerCase().includes('webhook') ); webhookNodes.forEach((node: any) => { // Vérifier si le webhook a une authentification const hasAuth = node.parameters?.authentication === true || (node.parameters?.authentication && node.parameters.authentication !== 'none'); if (!hasAuth) { issues.push({ message: `Le nœud webhook "${node.name}" n'a pas d'authentification configurée`, recommendation: 'Configurez l\'authentification pour les webhooks afin de limiter l\'accès', severity: strictness === 'low' ? 'medium' : 'high', location: node.id, }); } }); } /** * Vérifie l'utilisation des variables d'environnement pour les secrets */ private checkEnvironmentVariableUsage(workflow: any, issues: any[], strictness: string): void { if (!workflow.nodes || !Array.isArray(workflow.nodes)) { return; } // Compter le nombre de références aux variables d'environnement let envVarCount = 0; const workflowString = JSON.stringify(workflow); // Rechercher les références aux variables d'environnement (pattern: {{$env.VARIABLE_NAME}}) const envVarMatches = workflowString.match(/\{\{\$env\.[A-Za-z0-9_]+\}\}/g); envVarCount = envVarMatches ? envVarMatches.length : 0; // Pour les niveaux de rigueur medium et high, suggérer l'utilisation de variables d'environnement if (envVarCount === 0 && strictness !== 'low') { issues.push({ message: 'Le workflow n\'utilise pas de variables d\'environnement pour les secrets', recommendation: 'Utilisez des variables d\'environnement ({{$env.VARIABLE_NAME}}) pour stocker les informations sensibles', severity: strictness === 'medium' ? 'low' : 'medium', }); } } } export default SecurityValidator;

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/lowprofix/n8n-mcp-server'

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