This workflow follows the Emailsend → Postgres recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Flujo Fuzzing N8N",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-1744,
-32
],
"id": "51bf5b39-6fe1-40de-b7e9-5bf595ac1619",
"name": "When clicking \u2018Execute workflow\u2019"
},
{
"parameters": {
"jsCode": "// === CONFIGURACION (completar antes de ejecutar) ===\n// Lee la variable de entorno si existe; si no, usa el valor por defecto.\nconst getEnv = (k, def) => { try { return ($env && $env[k]) ? $env[k] : def; } catch (e) { return def; } };\nconst { execSync } = require('child_process');\nconst fs = require('fs');\n\n// CONFIGURACI\u00d3N DE RUTAS (Aseg\u00farate de que coincidan con tu PC)\nconst FFUF_PATH = getEnv('FFUF_PATH', 'ffuf');\nconst WORDLIST = getEnv('FFUF_WORDLIST', './wordlists/common.txt');\nconst TARGET = $('Loop Over Items').first().json.target\nconst OUTPUT_FILE = getEnv('WASA_TOOLS_DIR', './resultados') + '/ffuf_results.json';\nconst PHPSESSID = $('URL Ejemplo').first().json.phpsessionID;\n\ntry {\n // Ejecutar ffuf\n // -mc 200,301: Filtra solo resultados exitosos o redirecciones\n // -s: Modo silencioso para no ensuciar la consola\n // const command = `${FFUF_PATH} -u ${TARGET}/FUZZ -w ${WORDLIST} \"Cookie: PHPSESSID=${PHPSESSID}; security=low\" -mc 200,301 -s -of json`;\n\n const ffufCommand = `ffuf -u ${TARGET}/FUZZ -w ${WORDLIST} -mc 200,204,301,302,307,401,403 -s -of json -o ${OUTPUT_FILE} -t 100 -timeout 15 -H \"Cookie: PHPSESSID=${PHPSESSID}; security=low\"`;\n \n console.log(\"Ejecutando ffuf...\");\n execSync(ffufCommand);\n\n // Leer y parsear los resultados\n const rawData = fs.readFileSync(OUTPUT_FILE, 'utf8');\n const results = JSON.parse(rawData);\n\n return [{\n json: {\n message: \"Fuzzing de directorios completado\",\n ffufResults: results.results || [],\n discoveredPaths: results.results ? results.results.length : 0,\n totalFound: results.results.length,\n url: ffufCommand\n }\n }];\n\n} catch (error) {\n return [{ json: { error: \"Error en ffuf: \" + error.message } }];\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-384,
208
],
"id": "4a85c0ea-412d-4052-bd09-48b6f9968fb8",
"name": "ffuf"
},
{
"parameters": {
"jsCode": "const axios = require('axios');\n\nconst ZAP_API_KEY = $('Loop Over Items').first().json.zap_api_key;\nconst ZAP_URL = $('Loop Over Items').first().json.zap_url;\nconst TARGET_URL = $('Loop Over Items').first().json.target;\n\ntry {\n console.log(\"Iniciando Escaneo Activo (Active Scan)...\");\n\n // 1. Iniciar el Escaneo Activo\n // Nota: ZAP usar\u00e1 autom\u00e1ticamente la sesi\u00f3n (Cookie) porque inyectamos \n // la regla Replacer globalmente en el nodo del Spider.\n const activeScanResponse = await axios.get(`${ZAP_URL}/JSON/ascan/action/scan/`, {\n params: {\n apikey: ZAP_API_KEY,\n url: TARGET_URL,\n recurse: true,\n inScopeOnly: false\n }\n });\n \n const activeScanId = activeScanResponse.data.scan;\n console.log(`Active Scan ID: ${activeScanId}. Esperando a que finalice...`);\n\n // 2. Bucle de espera (con l\u00edmite de seguridad para evitar cuelgues infinitos)\n let progress = 0;\n let intentos = 0;\n const startTime = Date.now();\n const MAX_TIMEOUT_MS = 4.7 * 60 * 1000;\n const MAX_INTENTOS = 360; // 360 intentos * 5 seg = 30 minutos de m\u00e1ximo. \n \n while (progress < 100 && (Date.now() - startTime) < MAX_TIMEOUT_MS) {\n // Espera de 5 segundos\n await new Promise(resolve => setTimeout(resolve, 5000));\n \n try {\n const statusResponse = await axios.get(`${ZAP_URL}/JSON/ascan/view/status/`, {\n params: { apikey: ZAP_API_KEY, scanId: activeScanId }\n });\n \n progress = parseInt(statusResponse.data.status);\n } catch (error) {\n console.error(\"Error temporal al consultar ZAP:\", error.message);\n // Si hay un error de red, no rompemos el nodo, dejamos que intente de nuevo en 5s.\n }\n \n intentos++;\n \n // Logueamos solo cada 30 segundos (aprox 6 intentos) para no saturar la consola de n8n\n if (intentos % 6 === 0 || progress === 100) { \n const elapsedSeconds = Math.round((Date.now() - startTime) / 1000);\n console.log(`Progreso Escaneo Activo: ${progress}% (Tiempo transcurrido: ${elapsedSeconds}s)`);\n }\n }\n\n console.log(\"Obteniendo alertas de ZAP...\");\n\n // 3. Obtener alertas (Solo consulta la BD de ZAP, no requiere Headers de sesi\u00f3n)\n const response = await axios.get(`${ZAP_URL}/JSON/core/view/alerts/`, {\n params: {\n apikey: ZAP_API_KEY,\n baseurl: TARGET_URL\n }\n });\n\n // Filtramos como ten\u00edas antes (pluginId >= 100000 suele separar activas de pasivas)\n const rawAlerts = response.data.alerts || [];\n\n // ==========================================\n // MAGIA ANTI-MEMORIA: DEDUPLICACI\u00d3N DE ALERTAS\n // ==========================================\n // Nos aseguramos de no procesar 1000 veces la misma alerta exacta en la misma URL\n const uniqueAlertsMap = new Map();\n \n rawAlerts.forEach(a => {\n const uniqueKey = `${a.alert}_${a.url}`; // Llave \u00fanica: Nombre de vulnerabilidad + URL\n \n if (!uniqueAlertsMap.has(uniqueKey)) {\n uniqueAlertsMap.set(uniqueKey, {\n risk: a.risk,\n name: a.alert,\n url: a.url,\n evidence: a.evidence || 'N/A',\n description: a.description,\n alert: a.alert,\n solution: a.solution,\n cweid: a.cweid\n });\n }\n });\n\n // Convertimos el Map limpio de vuelta a un Array\n const allAlerts = Array.from(uniqueAlertsMap.values());\n\n // 4. Retornamos los datos respetando tu estructura exacta\n return [{\n json: {\n message: progress >= 100 ? \"Alertas obtenidas con \u00e9xito\" : \"Escaneo detenido por l\u00edmite de tiempo (Timeout)\",\n totalAlerts: allAlerts.length,\n highRisk: allAlerts.filter(a => a.risk === 'High').length,\n mediumRisk: allAlerts.filter(a => a.risk === 'Medium').length,\n full: rawAlerts, // Mantiene la lista cruda completa (por si la necesitas de respaldo)\n allAlerts: allAlerts // Manda la lista limpia y procesada para los nodos siguientes\n }\n }];\n\n} catch (error) {\n return [{ json: { error: \"Error en Active Scan: \" + error.message } }];\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-336,
16
],
"id": "d972578c-5bb2-4690-9c47-c9c7c1cd0b48",
"name": "Alertas ZAP"
},
{
"parameters": {
"jsCode": "// 1. Obtenemos el ID generado directamente del nodo 'Crear ID'\nconst scanId = $('Crear ID').first().json.id;\n\n// 2. Recuperamos la lista de vulnerabilidades del nodo \"Consolidar Resultado\"\n// Usamos $('Nombre del Nodo') para acceder a datos de nodos que no son el anterior directo\nconst vulnerabilidades = $('Consolidar Resultados Nuclei').first().json.vulnerabilities \n\n// 3. Mapeamos cada vulnerabilidad insertando el scan_id real\nconst itemsListos = vulnerabilidades.map(vuln => {\n return {\n json: {\n scan_id: scanId, // El ID real de la base de datos\n source: vuln.source,\n type: vuln.type,\n severity: vuln.severity,\n url: vuln.url,\n description: vuln.description,\n solution: vuln.solution,\n cweid: vuln.cweid || 0,\n evidence: vuln.evidence || 'N/A'\n }\n };\n});\n\nreturn itemsListos;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1808,
-272
],
"id": "45924053-0951-49b0-9a43-90c1d6c25d63",
"name": "Preparar las vulnerabilidades"
},
{
"parameters": {
"jsCode": "// === CONFIGURACION (completar antes de ejecutar) ===\n// Lee la variable de entorno si existe; si no, usa el valor por defecto.\nconst getEnv = (k, def) => { try { return ($env && $env[k]) ? $env[k] : def; } catch (e) { return def; } };\n// Generar reporte detallado\nconst data = $input.first().json;\nconst vulns = data.vulnerabilities;\nconst stats = $input.first().json.statistics;\n\nconst TARGET_URL = $('Loop Over Items').first().json.target\nconst scanId = $('Crear ID').first().json.id;\n\n// \u23f1\ufe0f C\u00e1lculo del tiempo de ejecuci\u00f3n total del flujo\nconst flowStartTime = $('URL Ejemplo').first().json.flowStartTime;\nconst flowEndTime = Date.now();\nconst durationMs = flowEndTime - flowStartTime;\n\nconst durationMinutes = Math.floor(durationMs / 60000);\nconst durationSeconds = Math.floor((durationMs % 60000) / 1000);\nconst durationFormatted = durationMinutes > 0\n ? `${durationMinutes} min ${durationSeconds} seg`\n : `${durationSeconds} seg`;\n\nconst report = `\n# \ud83d\udd0d Reporte de Fuzzing de Aplicaci\u00f3n Web\n\n**Fecha del Escaneo**: ${new Date(data.scanDate).toLocaleString('es-MX')}\n**Aplicaci\u00f3n Objetivo**: ${TARGET_URL}\n**Total de Vulnerabilidades**: ${stats.total}\n**Scan ID**: ${scanId}\n**\u23f1\ufe0f Tiempo de Ejecuci\u00f3n**: ${durationFormatted} (${(durationMs / 1000).toFixed(1)}s)\n\n---\n\n## \ud83d\udcca Resumen Ejecutivo\n\n| Severidad | Cantidad | Porcentaje |\n|-----------|----------|------------|\n| \ud83d\udd34 Cr\u00edtica | ${stats.critical} | ${((stats.critical/stats.total)*100).toFixed(1)}% |\n| \ud83d\udfe0 Alta | ${stats.high} | ${((stats.high/stats.total)*100).toFixed(1)}% |\n| \ud83d\udfe1 Media | ${stats.medium} | ${((stats.medium/stats.total)*100).toFixed(1)}% |\n| \ud83d\udfe2 Baja | ${stats.low} | ${((stats.low/stats.total)*100).toFixed(1)}% |\n\n---\n\n## \u23f1\ufe0f M\u00e9tricas de Ejecuci\u00f3n\n\n| M\u00e9trica | Valor |\n|---------|-------|\n| Inicio del escaneo | ${new Date(flowStartTime).toLocaleString('es-MX')} |\n| Fin del escaneo | ${new Date(flowEndTime).toLocaleString('es-MX')} |\n| Tiempo total de ejecuci\u00f3n | ${durationFormatted} |\n| Promedio por vulnerabilidad | ${stats.total > 0 ? (durationMs / stats.total / 1000).toFixed(2) + 's' : 'N/A'} |\n\n---\n\n## \ud83d\udea8 Vulnerabilidades Cr\u00edticas\n\n${vulns.filter(v => v.severity === 'critical').map((v, i) => `\n### ${i+1}. ${v.type}\n\n**URL**: ${v.url}\n**Fuente**: ${v.source}\n**CWE ID (OWASP Mapping)**: ${v.cweid ? 'CWE-' + v.cweid : 'N/A'}\n\n**Descripci\u00f3n**:\n${v.description}\n\n**Soluci\u00f3n Recomendada**:\n${v.solution}\n\n**Evidencia**:\n\\`\\`\\`\n${v.evidence ? v.evidence.substring(0, 200) : 'N/A'}\n\\`\\`\\`\n\n---\n`).join('\\n') || '*No se encontraron vulnerabilidades cr\u00edticas.*'}\n\n## \u26a0\ufe0f Vulnerabilidades Altas\n\n${vulns.filter(v => v.severity === 'high').slice(0, 10).map((v, i) => `\n### ${i+1}. ${v.type}\n\n**URL**: ${v.url}\n**Descripci\u00f3n**: ${v.description.substring(0, 150)}...\n**Soluci\u00f3n**: ${v.solution.substring(0, 150)}...\n\n---\n`).join('\\n') || '*No se encontraron vulnerabilidades altas.*'}\n\n${vulns.filter(v => v.severity === 'high').length > 5 ? `*...y ${vulns.filter(v => v.severity === 'high').length - 5} vulnerabilidades altas adicionales.*` : ''}\n\n## \ud83d\udccb Recomendaciones por Categor\u00eda\n\n${Object.entries(\n vulns.reduce((acc, v) => {\n acc[v.type] = (acc[v.type] || 0) + 1;\n return acc;\n }, {})\n).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([type, count]) =>\n `- **${type}**: ${count} ocurrencias`\n).join('\\n')}\n\n## \ud83d\udee1\ufe0f Plan de Remediaci\u00f3n\n\n1. **Prioridad Inmediata** (Cr\u00edticas y Altas):\n - Inyecciones SQL: Implementar prepared statements\n - XSS: Implementar output encoding\n - CSRF: Agregar tokens anti-CSRF\n\n2. **Corto Plazo** (Medias):\n - Configuraciones inseguras\n - Falta de headers de seguridad\n - Informaci\u00f3n sensible expuesta\n\n3. **Largo Plazo** (Bajas):\n - Mejoras en la documentaci\u00f3n de API\n - Optimizaci\u00f3n de mensajes de error\n\n---\n\n*Reporte generado autom\u00e1ticamente por sistema de fuzzing*\n*Herramientas utilizadas: OWASP ZAP, SQLMap, ffuf*\n*Tiempo total de ejecuci\u00f3n del flujo: ${durationFormatted}*\n`;\n\nconst fs = require('fs');\n\nconst reportPath = getEnv('WASA_REPORTS_DIR', './reportes') + `/Reporte_Final_${Date.now()}.md`;\n\nfs.writeFileSync(reportPath, report);\n\nreturn [{\n json: {\n report: report,\n reportPath: reportPath,\n statistics: stats,\n scanDate: $input.first().json.scanDate,\n vulns,\n executionTime: {\n startTime: flowStartTime,\n endTime: flowEndTime,\n durationMs: durationMs,\n durationFormatted: durationFormatted\n }\n }\n}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1088,
16
],
"id": "39716ce5-b2ee-4faf-9821-5355cc57cecb",
"name": "Reporte Final"
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "vulnerabilities",
"mode": "list",
"cachedResultName": "vulnerabilities"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"cweid": "={{ $json.cweid }}",
"source": "={{ $json.source }}",
"type": "={{ $json.type }}",
"severity": "={{ $json.severity }}",
"description": "={{ $json.description }}",
"url": "={{ $json.url }}",
"solution": "={{ $json.solution }}",
"evidence": "={{ $json.evidence }}",
"scan_id": "={{ $json.scan_id }}"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "scan_id",
"displayName": "scan_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "source",
"displayName": "source",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "type",
"displayName": "type",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "severity",
"displayName": "severity",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "url",
"displayName": "url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "description",
"displayName": "description",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "solution",
"displayName": "solution",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "cweid",
"displayName": "cweid",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "evidence",
"displayName": "evidence",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
2080,
-336
],
"id": "f4d273ff-dbdb-4dae-b603-2ce203b1a833",
"name": "Insertar Vulnerabilidades"
},
{
"parameters": {
"operation": "update",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "scans",
"mode": "list",
"cachedResultName": "scans"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"total_vulnerabilities": "={{ $('Reporte Final').item.json.statistics.total }}",
"critical_count": "={{ $('Reporte Final').item.json.statistics.critical }}",
"high_count": "={{ $('Reporte Final').item.json.statistics.high }}",
"medium_count": "={{ $('Reporte Final').item.json.statistics.medium }}",
"low_count": "={{ $('Reporte Final').item.json.statistics.low }}",
"target_url": "={{ $('Consolidar Resultados Nuclei').item.json.target }}",
"report_path": "={{ $('Reporte Final').item.json.reportPath }}",
"scan_date": "={{ $('Consolidar Resultados Nuclei').item.json.scanDate }}",
"id": "={{ $json.id }}"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "target_url",
"displayName": "target_url",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "scan_date",
"displayName": "scan_date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true
},
{
"id": "total_vulnerabilities",
"displayName": "total_vulnerabilities",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "critical_count",
"displayName": "critical_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "high_count",
"displayName": "high_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "medium_count",
"displayName": "medium_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "low_count",
"displayName": "low_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true
},
{
"id": "report_path",
"displayName": "report_path",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1584,
-224
],
"id": "66f2f490-9bb8-4574-8212-f5152fb83844",
"name": "Insertar Escaneos"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"numberInputs": 3,
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
576,
0
],
"id": "fa825de9-bd6a-4439-b89f-da184ad56ea5",
"name": "Combinaicon de Datos",
"alwaysOutputData": true
},
{
"parameters": {
"fromEmail": "tu-correo@example.com",
"toEmail": "tu-correo@example.com",
"subject": "=Reporte de Fuzzing",
"html": "={{ $json.correoFinal }}",
"options": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
1840,
224
],
"id": "c51a726e-9af3-4b6b-8a31-25eb65d7c76f",
"name": "Send email"
},
{
"parameters": {
"mode": "markdownToHtml",
"markdown": "={{ $json.report }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [
1360,
64
],
"id": "6acc8240-1370-4db8-a99e-cf93c7c1acf9",
"name": "reporteHTML"
},
{
"parameters": {
"jsCode": "// === CONFIGURACION (completar antes de ejecutar) ===\n// Lee la variable de entorno si existe; si no, usa el valor por defecto.\nconst getEnv = (k, def) => { try { return ($env && $env[k]) ? $env[k] : def; } catch (e) { return def; } };\nconst { exec } = require('child_process');\nconst util = require('util');\nconst fs = require('fs');\nconst process = require('process');\n\nconst execPromise = util.promisify(exec);\n\n// Definici\u00f3n de rutas (ajustar seg\u00fan el entorno donde corra n8n)\nconst NUCLEI_PATH = getEnv('NUCLEI_PATH', 'nuclei'); \nconst TEMPLATES_PATH = getEnv('NUCLEI_TEMPLATES', './nuclei-templates');\nconst OUTPUT_FILE = getEnv('WASA_TOOLS_DIR', './resultados') + '/nuclei_results_final.json';\n\nconst PHPSESSID = $('URL Ejemplo').first().json.phpsessionID;\nlet target = $('Loop Over Items').first().json.target;\nif (target.includes('localhost')) {\n target = target.replace('localhost', '127.0.0.1');\n}\n// Entorno unificado\nconst execOptions = {\n timeout: 270000,\n maxBuffer: 1024 * 1024 * 50, // 50MB\n env: {\n ...process.env,\n // Perfil de usuario del host (necesario para que Nuclei encuentre su config)\n USERPROFILE: getEnv('USERPROFILE', process.env.USERPROFILE),\n HOME: getEnv('HOME', process.env.HOME || process.env.USERPROFILE),\n APPDATA: process.env.APPDATA,\n LOCALAPPDATA: process.env.LOCALAPPDATA\n }\n};\n\ntry {\n console.log(\"Iniciando escaneo con Nuclei...\");\n\n // Limpiamos resultados viejos\n if (fs.existsSync(OUTPUT_FILE)) { \n fs.unlinkSync(OUTPUT_FILE); \n }\n\n // Usamos NUCLEI_PATH, agregamos filtro de severidad y usamos -je\n const simpleCommand = `\"${NUCLEI_PATH}\" -u \"${target}\" -t \"${TEMPLATES_PATH}\" -ni -silent -c 100 -rl 500 -H \"Cookie: PHPSESSID=${PHPSESSID}; security=low\" -je \"${OUTPUT_FILE}\"`;\n \n console.log(\"Ejecutando comando principal: \", simpleCommand);\n \n // EJECUCI\u00d3N AS\u00cdNCRONA\n try {\n await execPromise(simpleCommand, execOptions); \n } catch (execError) {\n console.log(\"Aviso de ejecuci\u00f3n de Nuclei (C\u00f3digo distinto a 0):\", execError.message);\n }\n\n // Leer y procesar JSON\n let results = [];\n const existeArchivo = fs.existsSync(OUTPUT_FILE);\n \n if (existeArchivo) {\n const rawData = fs.readFileSync(OUTPUT_FILE, 'utf8').trim();\n if (rawData) {\n try {\n const parsed = JSON.parse(rawData);\n results = Array.isArray(parsed) ? parsed : [parsed];\n } catch (e) {\n // Fallback para formato JSONL\n results = rawData.split('\\n')\n .filter(line => line.trim() !== '')\n .map(line => JSON.parse(line));\n }\n }\n }\n\n // Prueba de versi\u00f3n usando tambi\u00e9n la ruta absoluta\n let responseVersion = \"\";\n try {\n const { stdout, stderr } = await execPromise(`\"${NUCLEI_PATH}\" -version`, execOptions);\n responseVersion = (stdout || stderr).trim(); \n } catch (error) {\n responseVersion = error.message;\n }\n\n return [{\n json: {\n message: results.length > 0 ? \"\u00a1Vulnerabilidades detectadas!\" : \"Escaneo completado (Sin hallazgos)\",\n count: results.length,\n nucleiAlerts: results,\n debug: {\n existeArchivo,\n targetUsado: target,\n versionCheck: responseVersion\n }\n }\n }];\n\n} catch (error) {\n return [{ json: { error: \"Fallo cr\u00edtico en el nodo Nuclei: \" + error.message } }];\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-304,
-192
],
"id": "b6ace3c2-152b-4fec-8e2a-36a9b3f51210",
"name": "Nuclei Scann",
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "// Consolidar todos los hallazgos de las herramientas\nconst nucleiAlerts = $input.first().json.nucleiAlerts || []; // <-- Cambiado\nconst activeAlerts = $input.first().json.allAlerts || [];\nconst sqlmapResults = $input.first().json.sqlmap_results || [];\nconst ffufResults = $input.first().json.ffufResults || [];\n\nconst vulnerabilityMap = new Map();\n\n// [FIX 2026-07-27] Corregido el mapeo de severidad: el riskMap original\n// desplazaba cada nivel de ZAP un escal\u00f3n hacia arriba (High->critical),\n// lo que inflaba artificialmente el conteo de vulnerabilidades \"Cr\u00edtica\"\n// en el KPI del Dashboard. ZAP no emite nativamente el nivel \"Critical\",\n// por lo que High debe mapear a 'high', no a 'critical'.\nfunction classifySeverity(risk) {\n if (!risk) return 'low';\n const riskMap = {\n 'High': 'high',\n 'Medium': 'medium',\n 'Low': 'low',\n 'Informational': 'low',\n 'critical': 'critical',\n 'high': 'high',\n 'medium': 'medium',\n 'low': 'low',\n 'info': 'low'\n };\n return riskMap[risk] || risk || 'low';\n}\n\nfunction addOrUpdateVulnerability(vuln) {\n const key = `${vuln.type}`;\n if (!vulnerabilityMap.has(key)) {\n vulnerabilityMap.set(key, vuln);\n } else {\n const existing = vulnerabilityMap.get(key);\n if (vuln.source === 'SQLMap' || (vuln.evidence && !existing.evidence)) {\n vulnerabilityMap.set(key, { ...existing, ...vuln });\n }\n }\n}\n\n// 1. Procesar alertas Activas de ZAP\nactiveAlerts.forEach(alert => {\n addOrUpdateVulnerability({\n source: 'OWASP ZAP',\n type: alert.alert,\n severity: classifySeverity(alert.risk),\n url: alert.url,\n description: alert.description || alert.desc,\n solution: alert.solution,\n cweid: alert.cweid,\n evidence: alert.evidence\n });\n});\n\n// 2. Procesar alertas de Nuclei\nnucleiAlerts.forEach(alert => {\n addOrUpdateVulnerability({\n source: 'Nuclei',\n type: alert.info?.name || 'Vulnerabilidad Detectada',\n severity: classifySeverity(alert.info?.severity),\n url: alert['matched-at'] || alert.host || '',\n description: alert.info?.description || 'Detectado por esc\u00e1ner de plantillas Nuclei.',\n solution: alert.info?.remediation || 'Revisar la configuraci\u00f3n y aplicar parches recomendados.',\n // Nuclei agrupa los CWEs en un array, sacamos el n\u00famero\n cweid: alert.info?.classification?.['cwe-id']?.[0]?.replace('cwe-', '') || null,\n evidence: alert['extracted-results'] ? alert['extracted-results'].join(', ') : (alert.matcher_name || '')\n });\n});\n\n// 3. Procesar SQLMap\n// [FIX 2026-07-27] Severidad hardcodeada corregida de 'critical' a 'high':\n// una inyecci\u00f3n SQL confirmada corresponde a CWE-89 / Alto seg\u00fan la\n// clasificaci\u00f3n usada en el resto de la tesis (Tabla 8), no a Cr\u00edtico.\nsqlmapResults.filter(r => r.vulnerable).forEach(result => {\n addOrUpdateVulnerability({\n source: 'SQLMap',\n type: 'SQL Injection',\n severity: 'high',\n url: result.url,\n description: 'Vulnerabilidad de Inyecci\u00f3n SQL detectada mediante pruebas de carga automatizadas.',\n solution: 'Implementar consultas parametrizadas y validaci\u00f3n estricta de entradas.',\n cweid: 89,\n evidence: result.details\n });\n});\n\n// 4. Procesar ffuf\nffufResults.forEach(result => {\n if (result.status === 403 || result.status === 401) {\n addOrUpdateVulnerability({\n source: 'ffuf',\n type: 'Unauthorized Access Attempt',\n severity: 'low',\n url: result.url,\n description: `Se detect\u00f3 un recurso protegido con c\u00f3digo de estado: ${result.status}`,\n solution: 'Verificar que los controles de acceso y permisos de archivos sean los adecuados.',\n cweid: 284,\n evidence: `HTTP Status: ${result.status}`\n });\n }\n});\n\nconst allVulnerabilities = Array.from(vulnerabilityMap.values());\n\nconst stats = {\n total: allVulnerabilities.length,\n critical: allVulnerabilities.filter(v => v.severity === 'critical').length,\n high: allVulnerabilities.filter(v => v.severity === 'high').length,\n medium: allVulnerabilities.filter(v => v.severity === 'medium').length,\n low: allVulnerabilities.filter(v => v.severity === 'low').length\n};\n\nreturn [{\n json: {\n vulnerabilities: allVulnerabilities,\n statistics: stats,\n scanDate: new Date().toISOString(),\n target: $('Loop Over Items').first().json.target\n }\n }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
864,
16
],
"id": "20cf6f54-113f-4e6b-8e1c-f4aa44e4b7ec",
"name": "Consolidar Resultados Nuclei"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 40
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-1728,
208
],
"id": "5595db67-91c5-4702-a5f5-c2f1adcfd509",
"name": "Schedule Trigger"
},
{
"parameters": {
"jsCode": "// === CONFIGURACION (completar antes de ejecutar) ===\n// Lee la variable de entorno si existe; si no, usa el valor por defecto.\nconst getEnv = (k, def) => { try { return ($env && $env[k]) ? $env[k] : def; } catch (e) { return def; } };\nconst ZAP_API_KEY = getEnv('ZAP_API_KEY', 'REEMPLAZAR_CON_TU_ZAP_API_KEY');\nconst ZAP_URL = getEnv('ZAP_URL', 'http://localhost:8090');\nconst TARGET_DEMO = getEnv('WASA_TARGET_URL', 'http://localhost:8081/');\nconst PHPSESSID_DEMO= getEnv('WASA_PHPSESSID', 'REEMPLAZAR_CON_TU_PHPSESSID');\n// ===================================================\n\n// Detecta si esta corrida fue disparada por el Webhook Trigger (CHANGE-21).\n// Si s\u00ed, usa los datos reales del Bridge; si no (Manual/Schedule), mantiene\n// el comportamiento de prueba original hardcodeado a DVWA.\nlet webhookPayload = null;\ntry {\n const webhookItem = $('Webhook').first().json;\n webhookPayload = webhookItem.body ?? webhookItem;\n} catch (e) {\n webhookPayload = null;\n}\n\nconst flowStartTime = Date.now();\n\nif (webhookPayload && webhookPayload.target_url) {\n return [{\n json: {\n target: webhookPayload.target_url,\n nombre: \"WASA-Bridge\",\n zap_api_key: ZAP_API_KEY,\n zap_url: ZAP_URL,\n phpsessionID: webhookPayload.phpsessid,\n sqlmap_level: webhookPayload.sqlmap_level,\n sqlmap_risk: webhookPayload.sqlmap_risk,\n flowStartTime\n }\n }]\n}\n\n// Lista de aplicaciones a escanear\nconst objetivos = [\n { target: TARGET_DEMO, nombre: \"DVWA\" }\n];\n\n\n// Mapeamos para que n8n entienda que son \"items\" separados\nreturn objetivos.map(app => {\n return {\n json: {\n target: app.target,\n nombre: app.nombre,\n zap_api_key: ZAP_API_KEY,\n zap_url: ZAP_URL,\n phpsessionID: PHPSESSID_DEMO,\n sqlmap_level: 2,\n sqlmap_risk: 1,\n flowStartTime: flowStartTime // \u23f1\ufe0f Inicio del flujo completo\n }\n };\n});\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1536,
-80
],
"id": "f12daf9b-6c90-487a-8841-9cda346db011",
"name": "URL Ejemplo"
},
{
"parameters": {
"batchSize": "=1",
"options": {
"reset": false
}
},
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-1296,
-96
],
"id": "e26f8231-af7f-40c2-82b7-108a0db80f87",
"name": "Loop Over Items"
},
{
"parameters": {
"jsCode": "const htmlCrudo = $input.first().json.data;\n\nconst estilosCSS = `\n<div style=\"font-family: Arial, Helvetica, sans-serif; color: #333; max-width: 800px; margin: 0 auto; line-height: 1.6;\">\n <style>\n table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n th, td { border: 1px solid #dddddd; padding: 12px; text-align: left; }\n th { background-color: #f4f4f4; color: #333; }\n h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }\n h2, h3 { color: #34495e; }\n code { background-color: #f8f9fa; padding: 2px 4px; border-radius: 4px; font-family: monospace; }\n pre { background-color: #f8f9fa; padding: 15px; border-radius: 5px; overflow-x: auto; border: 1px solid #eee; }\n </style>\n ${htmlCrudo}\n</div>\n`;\n\nreturn [{\n json: {\n correoFinal: estilosCSS\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1600,
160
],
"id": "942859b6-8bc7-45e1-a0c9-7e78f0e9c1ec",
"name": "AddHTML"
},
{
"parameters": {
"operation": "push",
"list": "sqlmap_tasks",
"messageData": "={{ JSON.stringify({ \n\"urls\": $json.urlsFound, \n\"scan_id\": $('Crear ID').item.json.id,\n\"cookie\": \"PHPSESSID=\" + $('URL Ejemplo').item.json.phpsessionID + \"; security=low\",\n\"level\": $('URL Ejemplo').item.json.sqlmap_level,\n\"risk\": $('URL Ejemplo').item.json.sqlmap_risk\n}) }}",
"tail": true
},
"type": "n8n-nodes-base.redis",
"typeVersion": 1,
"position": [
240,
-304
],
"id": "5b23a901-c6e3-4e09-852a-f53b9f10ecf4",
"name": "Redis"
},
{
"parameters": {
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "scans",
"mode": "list",
"cachedResultName": "scans"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"target_url": "={{ $json.target }}"
},
"matchingColumns": [
"id"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "target_url",
"displayName": "target_url",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "scan_date",
"displayName": "scan_date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "total_vulnerabilities",
"displayName": "total_vulnerabilities",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "critical_count",
"displayName": "critical_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "high_count",
"displayName": "high_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "medium_count",
"displayName": "medium_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "low_count",
"displayName": "low_count",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "report_path",
"displayName": "report_path",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
-1008,
-128
],
"id": "a00a2c16-3a34-47f4-b882-6ab6830ce7bf",
"name": "Crear ID"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "fe715861-2bc7-49fb-bb5f-584b0eed4800",
"name": "id",
"value": "={{ $('Crear ID').first().json.id }}",
"type": "number"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1328,
-128
],
"id": "cc5e35f6-a3e5-439f-bfef-3a5130aaf27e",
"name": "Edit Fields"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "c466f568-d698-4eb2-9ce1-fa152838708c",
"leftValue": "={{ $json.urlsFound }}",
"rightValue": "?",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.3,
"position": [
-48,
-336
],
"id": "bec91cd4-3014-4a14-8f27-4fdb6d0c580b",
"name": "Filter"
},
{
"parameters": {
"fieldToSplitOut": "urlsFound",
"include": "allOtherFields",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
-304,
-368
],
"id": "1bf8cec8-3dcd-4498-8e61-698c1c65a859",
"name": "Item Lists"
},
{
"parameters": {
"jsCode": "const axios = require('axios');\n\nconst ZAP_API_KEY = $('URL Ejemplo').first().json.zap_api_key;\nconst ZAP_URL = $('URL Ejemplo').first().json.zap_url;\nconst TARGET_URL = $('Loop Over Items').first().json.target;\nconst PHPSESSID = $('URL Ejemplo').first().json.phpsessionID;\n\nconst COOKIE_AUTH = `PHPSESSID=${PHPSESSID}; security=low`;\n\ntry {\n console.log(\"Preparando entorno limpio en ZAP...\");\n\n // ==========================================\n // FIX 1: Crear una sesi\u00f3n nueva en ZAP para borrar el cach\u00e9 del \u00e1rbol de sitios\n // Esto asegura que ZAP empiece de cero y vuelva a escanear todo.\n // ==========================================\n await axios.get(`${ZAP_URL}/JSON/core/action/newSession/`, {\n params: { apikey: ZAP_API_KEY, name: 'n8n_Fuzzing_Session', overwrite: 'true' }\n });\n\n // ==========================================\n // FIX 2: Excluir URLs destructivas del Spider\n // Evitamos que ZAP visite logout.php (mata la sesi\u00f3n) y setup.php (resetea DVWA)\n // ==========================================\n const exclusions = ['.*logout.*', '.*setup\\\\.php.*'];\n for (const regex of exclusions) {\n await axios.get(`${ZAP_URL}/JSON/spider/action/excludeFromScan/`, {\n params: { apikey: ZAP_API_KEY, regex: regex }\n });\n }\n\n console.log(\"Configurando sesi\u00f3n (Replacer) en ZAP...\");\n\n // 1. LIMPIEZA PREVIA del Replacer\n try {\n await axios.get(`${ZAP_URL}/JSON/replacer/action/removeRule/`, {\n params: { apikey: ZAP_API_KEY, description: 'Sesion_DVWA' }\n });\n } catch (e) {\n // Si la regla no exist\u00eda, ignoramos\n }\n\n // 2. Inyectar la Cookie globalmente\n await axios.get(`${ZAP_URL}/JSON/replacer/action/addRule/`, {\n params: {\n apikey: ZAP_API_KEY,\n description: 'Sesion_DVWA',\n enabled: 'true',\n matchType: 'REQ_HEADER',\n matchRegex: 'false',\n matchString: 'Cookie',\n replacement: COOKIE_AUTH\n }\n });\n\n // 3. Iniciar el Spider\n const spiderResponse = await axios.get(`${ZAP_URL}/JSON/spider/action/scan/`, {\n params: {\n apikey: ZAP_API_KEY,\n url: TARGET_URL,\n maxChildren: 10,\n recurse: true\n }\n });\n\n const scanId = spiderResponse.data.scan;\n console.log(`Iniciando Spider... ID: ${scanId}`);\n\n // 4. Bucle de espera hasta que el Spider termine\n let progress = 0;\n while (progress < 100) {\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n const statusResponse = await axios.get(`${ZAP_URL}/JSON/spider/view/status/`, {\n params: { apikey: ZAP_API_KEY, scanId: scanId }\n });\n\n progress = parseInt(statusResponse.data.status);\n console.log(`Progreso ZAP: ${progress}%`);\n }\n\n // 5. Extraer los resultados\n const urlsResponse = await axios.get(`${ZAP_URL}/JSON/spider/view/results/`, {\n params: { apikey: ZAP_API_KEY, scanId: scanId }\n });\n\n const rawUrls = urlsResponse.data.results || [];\n \n // Usamos Set para eliminar todas las URLs repetidas. \n const uniqueUrls = [...new Set(rawUrls)];\n\n return [{\n json: {\n message: \"Spider completado con sesi\u00f3n activa y entorno limpio\",\n urlsFound: uniqueUrls,\n count: uniqueUrls.length\n }\n }];\n\n} catch (error) {\n // Mejora opcional: imprimir el error exacto de Axios si existe\n const errorMsg = error.response ? JSON.stringify(error.response.data) : error.message;\n return [{ json: { error: errorMsg } }];\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-720,
-144
],
"id": "e296cfa5-7b16-4575-976d-f0792be0f97d",
"name": "ZAP Spider (Descubrimiento)"
},
{
"parameters": {
"httpMethod": "POST",
"path": "wasa-scan",
"authentication": "headerAuth",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-1824,
-304
],
"id": "8e84818b-6a9d-4f68-be0a-3a9964f27ff1",
"name": "Webhook"
}
],
"connections": {
"ffuf": {
"main": [
[
{
"node": "Combinaicon de Datos",
"type": "main",
"index": 2
}
]
]
},
"Alertas ZAP": {
"main": [
[
{
"node": "Combinaicon de Datos",
"type": "main",
"index": 1
}
]
]
},
"Preparar las vulnerabilidades": {
"main": [
[
{
"node": "Insertar Vulnerabilidades",
"type": "main",
"index": 0
}
]
]
},
"Reporte Final": {
"main": [
[
{
"node": "reporteHTML",
"type": "main",
"index": 0
},
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Combinaicon de Datos": {
"main": [
[
{
"node": "Consolidar Resultados Nuclei",
"type": "main",
"index": 0
}
]
]
},
"reporteHTML": {
"main": [
[
{
"node": "AddHTML",
"type": "main",
"index": 0
}
]
]
},
"Insertar Escaneos": {
"main": [
[
{
"node": "Preparar las vulnerabilidades",
"type": "main",
"index": 0
}
]
]
},
"Nuclei Scann": {
"main": [
[
{
"node": "Combinaicon de Datos",
"type": "main",
"index": 0
}
]
]
},
"Consolidar Resultados Nuclei": {
"main": [
[
{
"node": "Reporte Final",
"type": "main",
"index": 0
}
]
]
},
"URL Ejemplo": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Crear ID",
"type": "main",
"index": 0
}
]
]
},
"Send email": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"AddHTML": {
"main": [
[
{
"node": "Send email",
"type": "main",
"index": 0
}
]
]
},
"Crear ID": {
"main": [
[
{
"node": "ZAP Spider (Descubrimiento)",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Insertar Escaneos",
"type": "main",
"index": 0
}
]
]
},
"Filter": {
"main": [
[
{
"node": "Redis",
"type": "main",
"index": 0
}
]
]
},
"Item Lists": {
"main": [
[
{
"node": "Filter",
"type": "main",
"index": 0
}
]
]
},
"ZAP Spider (Descubrimiento)": {
"main": [
[
{
"node": "ffuf",
"type": "main",
"index": 0
},
{
"node": "Alertas ZAP",
"type": "main",
"index": 0
},
{
"node": "Nuclei Scann",
"type": "main",
"index": 0
},
{
"node": "Item Lists",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "URL Ejemplo",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "URL Ejemplo",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "URL Ejemplo",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"timeSavedMode": "fixed",
"callerPolicy": "workflowsFromSameOwner",
"executionTimeout": -1,
"availableInMCP": false
},
"versionId": "66ae04aa-7fc9-4cc7-8814-c95e51eaf22d",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "EdcXkTtlH5xIMwre",
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Flujo Fuzzing N8N. Uses postgres, emailSend, redis. Event-driven trigger; 23 nodes.
Source: https://github.com/Nickolan/Wasa-System/blob/eee4c8687ed0322b483c251bc60125a5cf252c09/Herramientas/Flujo_Fuzzing_N8N.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
aula-00-mapa-do-n8n. Uses emailReadImap, stopAndError, httpRequest, graphql. Event-driven trigger; 46 nodes.
Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.
Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.
Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.
Bill Payment Automated Invoices (Contractor invoice). Uses httpRequest, googleSheets, emailSend, postgres. Event-driven trigger; 25 nodes.