AutomationFlowsData & Sheets › Flujo Fuzzing N8n

Flujo Fuzzing N8n

Flujo Fuzzing N8N. Uses postgres, emailSend, redis. Event-driven trigger; 23 nodes.

Event trigger★★★★☆ complexity23 nodesPostgresEmail SendRedis
Data & Sheets Trigger: Event Nodes: 23 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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": []
}
Pro

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 →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

aula-00-mapa-do-n8n. Uses emailReadImap, stopAndError, httpRequest, graphql. Event-driven trigger; 46 nodes.

Email Read Imap, Stop And Error, HTTP Request +14
Data & Sheets

Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.

N8N Nodes Evolution Api, Redis, HTTP Request +3
Data & Sheets

Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.

Execute Workflow Trigger, Redis, HTTP Request +3
Data & Sheets

Bill Payment Automated Invoices (Contractor invoice). Uses httpRequest, googleSheets, emailSend, postgres. Event-driven trigger; 25 nodes.

HTTP Request, Google Sheets, Email Send +1