CyberPulse Cumplimiento – Pipeline de procesamiento por lotes v2

Avanzado

Este es unautomatización que contiene 18 nodos.Utiliza principalmente nodos como Set, Code, Merge, GoogleSheets, ManualTrigger. Puntuación automatizada de controles de cumplimiento usando CyberPulse, GPT-4o y Google Sheets

Requisitos previos
  • Credenciales de API de Google Sheets
  • Clave de API de OpenAI

Categoría

-
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
  "id": "6yJ0KXSSGqSb9FY6",
  "meta": {
    "instanceId": "6feff41aadeb8409737e26476f9d0a45f95eec6a9c16afff8ef87a662455b6df",
    "templateCredsSetupCompleted": true
  },
  "name": "CyberPulse Compliance – v2 batch pipeline",
  "tags": [],
  "nodes": [
    {
      "id": "1ae2a107-6515-4dbe-a3b8-4c3fd5d64cce",
      "name": "Activador manual",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1008,
        -608
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "2360ade2-1686-4554-beb3-76ba59e16408",
      "name": "Agregar fila en hoja",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2208,
        -336
      ],
      "parameters": {
        "columns": {
          "value": {
            "score": "={{ $json.score }}",
            "status": "={{ $json.status }}",
            "rationale": "={{ $json.rationale }}",
            "timestamp": "={{ $json.timestamp }}",
            "ai_summary": "={{ $json.ai_summary }}",
            "categories": "={{ $json.categories }}",
            "confidence": "={{ $json.confidence }}",
            "control_id": "={{ $json.control_id }}",
            "evaluation": "={{ $json.evaluation }}",
            "ai_findings": "={{ $json.ai_findings }}",
            "mapped_count": "={{ $json.mapped_count }}",
            "mapping_flat": "={{ $json.mapping_flat }}",
            "response_text": "={{ $json.response_text }}",
            "engine_version": "={{ $json.engine_version }}",
            "evidence_count": "={{ $json.evidence_count }}",
            "evidence_url_1": "={{ $json.evidence_url_1 }}",
            "evidence_url_2": "={{ $json.evidence_url_2 }}",
            "evidence_url_3": "={{ $json.evidence_url_3 }}",
            "evidence_url_4": "={{ $json.evidence_url_4 }}",
            "ai_recommendations": "={{ $json.ai_recommendations }}",
            "control_description": "={{ $json.control_description }}",
            "frameworks_selected": "={{ $json.frameworks_selected }}",
            "implementation_notes": "={{ $json.implementation_notes }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "control_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "control_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "control_description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "control_description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "response_text",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "response_text",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "implementation_notes",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "implementation_notes",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_url_1",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_url_1",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_url_2",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_url_2",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_url_3",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_url_3",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_url_4",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_url_4",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evaluation",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evaluation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "score",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "score",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "confidence",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "confidence",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "rationale",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "rationale",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "categories",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "categories",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mapped_count",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mapped_count",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "mapping_flat",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "mapping_flat",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "frameworks_selected",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "frameworks_selected",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "engine_version",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "engine_version",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_summary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ai_summary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_findings",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ai_findings",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ai_recommendations",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ai_recommendations",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "owner",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "owner",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "due_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "due_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ticket_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ticket_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "next_action",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "next_action",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "evidence_gap_flag",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "evidence_gap_flag",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "policy_gap_flag",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "policy_gap_flag",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "last_run_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "last_run_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "source_sheet_row",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "source_sheet_row",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1117838353,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1owd3qVXCO34EhvBO5Vr3d2Pn2_QQXt0rNl_kroSmf0I/edit#gid=1117838353",
          "cachedResultName": "controls_results_template.csv"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1owd3qVXCO34EhvBO5Vr3d2Pn2_QQXt0rNl_kroSmf0I",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1owd3qVXCO34EhvBO5Vr3d2Pn2_QQXt0rNl_kroSmf0I/edit?usp=drivesdk",
          "cachedResultName": "controls_results_template"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "SHFVDGHEaA6jzLc4",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "46fe1437-f8b0-4ede-9cc6-23958351755a",
      "name": "Obtener fila(s) en hoja",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -992,
        -368
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1182991363,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Gcak2OCpo2vnDkB2W49xz4TD762DGCY3ZA6Pkfv82nM/edit#gid=1182991363",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Gcak2OCpo2vnDkB2W49xz4TD762DGCY3ZA6Pkfv82nM",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Gcak2OCpo2vnDkB2W49xz4TD762DGCY3ZA6Pkfv82nM/edit?usp=drivesdk",
          "cachedResultName": "controls_input_mock_100_rows"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "SHFVDGHEaA6jzLc4",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "2f4910f6-3fdf-4133-b714-b6752d5bdb94",
      "name": "Explicar y recomendar",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        640,
        -288
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "=Use only the provided fields. Do not invent evidence, numbers, or frameworks.\nReturn a JSON object with keys exactly:\n- ai_summary (string)\n- ai_findings (array of 3 short strings), no bullets, no dashes, no numbering, no checkboxes.\n- ai_recommendations (array of 3 short, actionable strings), no bullets, no dashes, no numbering, no checkboxes.\nNo other keys.\n"
            },
            {
              "content": "={\n  \"timestamp\": \"{{ $('Edit Fields').item.json.timestamp }}\",\n  \"control_id\": \"{{ $('Edit Fields').item.json.control_id }}\",\n  \"control_description\": \"{{ $('Edit Fields').item.json.control_description }}\",\n  \"response_text\": \"{{ $('Edit Fields').item.json.response_text }}\",\n  \"implementation_notes\": \"{{$json.implementation_notes || ''}}\",\n\n  \"status\": \"{{$json.status}}\",\n  \"evaluation\": \"{{$json.evaluation}}\",\n  \"score\": {{ +$json.score }},\n  \"confidence\": {{ +$json.confidence }},\n  \"rationale\": \"{{$json.rationale}}\",\n\n  \"evidence_count\": {{\n  Array.isArray($json.evidence)\n    ? $json.evidence.filter(u => u && String(u).trim()).length\n    : ([\n        $json.evidence_url_1,\n        $json.evidence_url_2,\n        $json.evidence_url_3,\n        $json.evidence_url_4\n      ].filter(u => u && String(u).trim()).length || ($json.evidence_count ?? 0))\n}},\n\n  \"mapped_count\": {{ Array.isArray($json.mapped_requirements) ? $json.mapped_requirements.length : ($json.mapped_count ?? 0) }},\n  \"mapping_flat\": \"{{ Array.isArray($json.mapped_requirements) ? $json.mapped_requirements.map(m => [m.framework, m.clause, m.title].filter(Boolean).join(': ')).join(' | ') : ($json.mapping_flat || '') }}\",\n  \"categories\": \"{{ Array.isArray($json.categories) ? $json.categories.join(', ') : ($json.categories || '') }}\",\n  \"frameworks_selected\": \"{{ Array.isArray($json.mapped_requirements) ? [...new Set($json.mapped_requirements.map(m => m.framework).filter(Boolean))].join(', ') : ($json.frameworks_selected || '') }}\",\n  \"engine_version\": \"{{$json.engine_version || ''}}\",\n\n  \"format_instructions\": \"Return ai_summary exactly as: 'Status: {status}. Evaluation: {evaluation}. Score: {score}. Confidence: {confidence}. Evidence items: {evidence_count}. Categories: {categories}. Mappings: {mapping_flat}'. Then return ai_findings as a single string with 3 short bullets (prefix each with '• ') grounded in rationale/categories. Then return ai_recommendations as a single string with 3 short, actionable bullets (prefix each with '• ') tied to mapping_flat and evidence_count. Also compute priority, next_action, evidence_gap_flag, policy_gap_flag using the rules below.\\n\\nRules:\\n- priority:\\n  - P1 if (status == 'Non-Compliant' && mapped_count >= 3) OR (score < 50 && evidence_count == 0)\\n  - P2 if (status in ['Non-Compliant','Partial'] && (score >= 50 && score < 75)) OR (evidence_count <= 1)\\n  - P3 if (status == 'Compliant' && (confidence < 60 || evidence_count < 2))\\n  - else P4\\n- next_action:\\n  - 'implement' if status == 'Non-Compliant'\\n  - 'remediate' if status == 'Partial'\\n  - 'monitor' if status == 'Compliant' and (confidence < 60 || evidence_count < 2)\\n  - else 'review'\\n- evidence_gap_flag: 'yes' if evidence_count == 0 OR evidence_count == 1, else 'no'\\n- policy_gap_flag: 'yes' if ('policy' appears in (response_text or rationale) case-insensitive) OR categories contains 'policy', else 'no'\"\n}\n"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "id": "gcqJ07oLkr9oSi82",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "1920b78c-97ac-4303-a055-e2541cf12f29",
      "name": "Analizar + adjuntar a cada elemento",
      "type": "n8n-nodes-base.code",
      "position": [
        1696,
        -336
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Helpers\nfunction isNonEmpty(x){ if(x===undefined||x===null) return false; if(typeof x==='number') return true; return String(x).trim()!==''; }\nfunction prefer(...vals){ for(const v of vals){ if(isNonEmpty(v)) return v; } return ''; }\nfunction safeNum(x, def=''){ const n = Number(x); return Number.isFinite(n) ? n : def; }\n\n// Strip any leading bullets / dashes / numbers / checkboxes before we add our own bullets\nfunction stripLeadingMarkers(s){\n  return String(s).replace(/^\\s*(?:[-*•\\u2022]+|\\d+[.)]|[✓✔✗✘xX\\[\\]\\(\\)])\\s*/u, '');\n}\nfunction asArray(x){\n  if(!x) return [];\n  if(Array.isArray(x)) return x.filter(Boolean).map(v => stripLeadingMarkers(v));\n  return String(x).split(/\\r?\\n+/).map(v => stripLeadingMarkers(v)).filter(Boolean);\n}\nfunction bullets(x){\n  const a = asArray(x);\n  return a.length ? a.map(s => `• ${s}`).join('\\n') : '';\n}\n\n// Current item from Merge\nconst i = $json;\n// Originals straight from \"Edit Fields\" (for fields the merge might not include)\nconst src = $(\"Edit Fields\").item?.json ?? {};\n\n// ---- Ingest (preserve originals)\nconst timestamp           = prefer(i.timestamp, src.timestamp, new Date().toISOString());\nconst control_id          = prefer(i.control_id, src.control_id);\nconst control_description = prefer(i.control_description, src.control_description);\nconst response_text       = prefer(i.response_text, src.response_text);\nconst implementation_notes= prefer(i.implementation_notes, src.implementation_notes);\nconst evidence_url_1      = prefer(i.evidence_url_1, src.evidence_url_1);\nconst evidence_url_2      = prefer(i.evidence_url_2, src.evidence_url_2);\nconst evidence_url_3      = prefer(i.evidence_url_3, src.evidence_url_3);\nconst evidence_url_4      = prefer(i.evidence_url_4, src.evidence_url_4);\n\n// ---- Scoring & mapping\nconst status       = prefer(i.status, 'Unknown');\nconst evaluation   = prefer(i.evaluation, status);\nconst score        = safeNum(i.score);\nconst confidence   = safeNum(i.confidence);\nconst rationaleIn  = prefer(i.rationale, i.reason);\n\n// categories may be array or string\nconst categoriesStr = Array.isArray(i.categories) ? i.categories.join(', ') : prefer(i.categories);\n\n// evidence: prefer i.evidence (array), else derive from URL fields, then filter empties\nlet evidenceArr = [];\nif (Array.isArray(i.evidence)) {\n  evidenceArr = i.evidence.filter(u => u && String(u).trim());\n} else {\n  evidenceArr = [evidence_url_1, evidence_url_2, evidence_url_3, evidence_url_4]\n    .filter(u => u && String(u).trim());\n}\nconst evidence_count = isNonEmpty(i.evidence_count) ? safeNum(i.evidence_count, evidenceArr.length) : evidenceArr.length;\n\n// mapped requirements\nconst mappedReqs   = Array.isArray(i.mapped_requirements) ? i.mapped_requirements : [];\nconst mapped_count = isNonEmpty(i.mapped_count) ? safeNum(i.mapped_count, mappedReqs.length) : mappedReqs.length;\nconst mapping_flat = isNonEmpty(i.mapping_flat)\n  ? String(i.mapping_flat)\n  : mappedReqs.map(m => [m.framework, m.clause, m.title].filter(Boolean).join(': ')).join(' | ');\n\n// frameworks selected (pretty commas)\nconst frameworks_selected = (isNonEmpty(i.frameworks_selected)\n  ? String(i.frameworks_selected)\n  : [...new Set(mappedReqs.map(m => m.framework).filter(Boolean))].join(', ')\n).replace(/,\\s*/g, ', ');\n\nconst engine_version = prefer(i.engine_version, i.version);\n\n// ---- AI outputs (handle message.content object OR JSON string; also accept top-level)\nlet ai_summary = i.ai_summary;\nlet ai_findings_any = i.ai_findings;\nlet ai_recommendations_any = i.ai_recommendations;\n\nif ((!ai_summary || (!ai_findings_any && !ai_recommendations_any)) && i.message?.content) {\n  if (typeof i.message.content === 'object') {\n    const c = i.message.content;\n    ai_summary = ai_summary ?? c.ai_summary;\n    ai_findings_any = ai_findings_any ?? c.ai_findings;\n    ai_recommendations_any = ai_recommendations_any ?? c.ai_recommendations;\n  } else {\n    try {\n      const parsed = JSON.parse(i.message.content);\n      ai_summary = ai_summary ?? parsed.ai_summary;\n      ai_findings_any = ai_findings_any ?? parsed.ai_findings;\n      ai_recommendations_any = ai_recommendations_any ?? parsed.ai_recommendations;\n    } catch {}\n  }\n}\n\n// Normalize to single strings for the sheet (no double bullets)\nconst ai_findings        = bullets(ai_findings_any);\nconst ai_recommendations = bullets(ai_recommendations_any);\n\n// Sync the item count inside rationale (e.g., replace \"(4 items)\" with \"(3 items)\")\nlet rationale = rationaleIn;\nif (isNonEmpty(rationale)) {\n  rationale = rationale.replace(/\\(\\s*\\d+\\s*items?\\s*\\)/i, `(${evidence_count} items)`);\n}\n\n// Synthesize summary if missing (deterministic)\nif (!isNonEmpty(ai_summary)) {\n  ai_summary = `Status: ${status}. Evaluation: ${evaluation}. Score: ${score}. `\n             + `Confidence: ${confidence}. Evidence items: ${evidence_count}. `\n             + `Categories: ${categoriesStr}. Mappings: ${mapping_flat}`;\n}\n\n// Return final row payload\nreturn {\n  json: {\n    // Ingest\n    timestamp,\n    control_id,\n    control_description,\n    response_text,\n    implementation_notes,\n    evidence_url_1,\n    evidence_url_2,\n    evidence_url_3,\n    evidence_url_4,\n\n    // Scoring & mapping\n    status,\n    evaluation,\n    score,\n    confidence,\n    rationale,\n    categories: categoriesStr,\n    evidence_count,\n    mapped_count,\n    mapping_flat,\n    frameworks_selected,\n    engine_version,\n\n    // AI (strings)\n    ai_summary,\n    ai_findings,\n    ai_recommendations\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "23954e15-29e1-49d3-9614-772e33562fc4",
      "name": "Iterar sobre elementos",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        160,
        -368
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "1f2dda49-fde7-44f8-8efd-e6ffd73e4e96",
      "name": "Editar campos",
      "type": "n8n-nodes-base.set",
      "position": [
        -672,
        -368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "87318d44-cd2c-47d8-850a-c0b152d6b455",
              "name": "row_number",
              "type": "string",
              "value": "={{ $json.row_number }}"
            },
            {
              "id": "9eab7a02-e58e-46a8-85cb-d2a41693759c",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $json.timestamp }}"
            },
            {
              "id": "6d3fa2f3-5e8b-45c6-80f6-e9b713fd82e4",
              "name": "control_description",
              "type": "string",
              "value": "={{ $json.control_description }}"
            },
            {
              "id": "e0b7a487-bc52-40d9-80a6-5bc2a13c19a0",
              "name": "response_text",
              "type": "string",
              "value": "={{ $json.response_text }}"
            },
            {
              "id": "b6347812-76af-454b-8869-81c47f4c90a8",
              "name": "evidence_url_1",
              "type": "string",
              "value": "={{ $json.evidence_url_1 }}"
            },
            {
              "id": "f8784a98-ead8-4af2-a1ca-42aa2fcc6032",
              "name": "evidence_url_2",
              "type": "string",
              "value": "={{ $json.evidence_url_2 }}"
            },
            {
              "id": "d91dceb0-e1a7-4322-89a9-041a34e00f52",
              "name": "evidence_url_3",
              "type": "string",
              "value": "={{ $json.evidence_url_3 }}"
            },
            {
              "id": "14d6374b-5191-4f4f-85b0-d443a9e404ee",
              "name": "evidence_url_4",
              "type": "string",
              "value": "={{ $json.evidence_url_4 }}"
            },
            {
              "id": "b8bd82a7-c19c-458b-a0cb-5cf2941efc0c",
              "name": "implementation_notes",
              "type": "string",
              "value": "={{ $json.implementation_notes }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "af2a5b70-b8e7-4ab4-b9cb-29da669d1474",
      "name": "Combinar1",
      "type": "n8n-nodes-base.merge",
      "position": [
        1216,
        -336
      ],
      "parameters": {
        "mode": "combine",
        "options": {
          "includeUnpaired": true
        },
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d2232908-5186-4e0a-b771-7230028b8b43",
      "name": "CyberPulse Compliance (Dev)",
      "type": "n8n-nodes-cyberpulse-compliance-dev.cyberPulseCompliance",
      "position": [
        -288,
        -368
      ],
      "parameters": {
        "controlText": "=={{ $json.response_text + ($json.implementation_notes ? (' ' + $json.implementation_notes) : '') }}",
        "crosswalkUrl": "https://gist.githubusercontent.com/gitadta/c6b7b69ae2a00f2a67e3bbac4b6648d4/raw/238ce80b0252702d4e6e9c19015bf958d0a0bad6/crosswalk.json",
        "evidenceUrls": [
          "={{ $json.evidence_url_1 }}",
          "={{ $json.evidence_url_2 }}",
          "={{ $json.evidence_url_3 }}",
          "={{ $json.evidence_url_4 }}"
        ]
      },
      "credentials": {
        "cyberPulseHttpHeaderAuthApi": {
          "id": "s8WtV23qQ1z0bVTT",
          "name": "CyberPulse HTTP Header Auth account"
        }
      },
      "typeVersion": 6
    },
    {
      "id": "a7a4f9e1-df82-4776-827b-e89177d9c935",
      "name": "Nota adhesiva",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1328,
        -704
      ],
      "parameters": {
        "color": 4,
        "width": 528,
        "height": 512,
        "content": "\n\n🟢 Manual Trigger — Start/diagnostics\n\nReceives POST and starts the run; echoes runId for tracing\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n🟩 Get row(s) in sheet — Read inputs\n\nLoads model/routing settings from\n the config sheet."
      },
      "typeVersion": 1
    },
    {
      "id": "95cc8cf7-0b2b-4e33-974c-bcb23d07c265",
      "name": "Nota adhesiva1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -784,
        -576
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 384,
        "content": "\n\n\n\n\n🟦 Edit Fields — Normalize columns\n\nMaps incoming keys, trims text, and sets safe defaults."
      },
      "typeVersion": 1
    },
    {
      "id": "f98d67c1-31e3-4274-a0b0-41812290b18a",
      "name": "Nota adhesiva2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        -576
      ],
      "parameters": {
        "width": 432,
        "height": 384,
        "content": "\n\n🟨 CyberPulse Compliance (Dev) — Score + map\n\nScores and maps each control using control text + implementation notes + up to 4 evidence URLs and selected frameworks.\nOutputs score (0–100), status, confidence, binary evaluation, categories, crosswalk mappings, and adds gaps/actions when evidence is weak or missing."
      },
      "typeVersion": 1
    },
    {
      "id": "0777506c-4b63-45ae-a27e-2c4fa519947c",
      "name": "Nota adhesiva3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -576
      ],
      "parameters": {
        "color": 6,
        "width": 432,
        "height": 384,
        "content": "\n\n🟪 Loop Over Items — Iterate per control\n\nIterates each control independently to run LLM → parse → append, preserving per-row context and emitting one result per input."
      },
      "typeVersion": 1
    },
    {
      "id": "f5161050-7010-43d6-aadc-eec7daae85d0",
      "name": "Nota adhesiva4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        528,
        -576
      ],
      "parameters": {
        "color": 5,
        "width": 512,
        "height": 384,
        "content": "\n\n🟦 Explain & Recommend (Message Model) — Exec summary\n\nGenerates a strict-JSON executive summary—ai_summary, three ai_findings, and three ai_recommendations—from the control’s status, score, confidence, categories, evidence, and framework mappings."
      },
      "typeVersion": 1
    },
    {
      "id": "ed04125f-e8d8-4a05-afe0-3f8844f07f5e",
      "name": "Nota adhesiva5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        -576
      ],
      "parameters": {
        "color": 3,
        "width": 464,
        "height": 384,
        "content": "\n\n🟧 Merge1 — Combine model + original\n\nCombines CyberPulse scoring/mapping with the LLM summary by position, producing one merged item per row."
      },
      "typeVersion": 1
    },
    {
      "id": "16c54ee5-0dc1-4374-b2f9-ca831de1c1a4",
      "name": "Nota adhesiva6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1536,
        -576
      ],
      "parameters": {
        "width": 464,
        "height": 384,
        "content": "\n\n🟨 Parse + attach to each item — Final shaping\n\nMerges CyberPulse scores/mappings with the LLM output by index into a single unified row."
      },
      "typeVersion": 1
    },
    {
      "id": "b4e8b1ee-95ad-4a8b-bd3b-2c07ecab42b3",
      "name": "Nota adhesiva7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2016,
        -576
      ],
      "parameters": {
        "color": 4,
        "width": 496,
        "height": 384,
        "content": "\n\n🟩 Append row in sheet — Write results\n\nAppends one result row per item to the results sheet with core, scoring, mapping, and AI fields, leaving future columns blank."
      },
      "typeVersion": 1
    },
    {
      "id": "297e3ea4-35bd-4bc5-a851-ecf0f8dda5bc",
      "name": "Nota adhesiva8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        -912
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 288,
        "content": "\n\nWhat is CyberPulse Agent workflow ?\n\nAutomates evidence-aware control scoring (0–100) with deterministic gates and confidence from evidence/text quality.\nReads controls from Google Sheets (text, up to 4 evidence URLs, notes) and classifies, scores, and maps via the CyberPulse node.\nGenerates board-ready AI outputs per control: one-paragraph summary, 3 findings, and 3 actionable recommendations.\nWrites normalized, analytics-ready rows back to a results sheet with flattened framework mappings and detected categories.\nCovers ISO 27001, NIST CSF, SOC 2, PCI DSS, Essential Eight, GDPR; secure, scalable in n8n with tunable weights/thresholds."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "8a012f28-964c-4087-9b74-7c24a4d8f76e",
  "connections": {
    "af2a5b70-b8e7-4ab4-b9cb-29da669d1474": {
      "main": [
        [
          {
            "node": "1920b78c-97ac-4303-a055-e2541cf12f29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1f2dda49-fde7-44f8-8efd-e6ffd73e4e96": {
      "main": [
        [
          {
            "node": "d2232908-5186-4e0a-b771-7230028b8b43",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1ae2a107-6515-4dbe-a3b8-4c3fd5d64cce": {
      "main": [
        [
          {
            "node": "46fe1437-f8b0-4ede-9cc6-23958351755a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "23954e15-29e1-49d3-9614-772e33562fc4": {
      "main": [
        [],
        [
          {
            "node": "2f4910f6-3fdf-4133-b714-b6752d5bdb94",
            "type": "main",
            "index": 0
          },
          {
            "node": "af2a5b70-b8e7-4ab4-b9cb-29da669d1474",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2360ade2-1686-4554-beb3-76ba59e16408": {
      "main": [
        [
          {
            "node": "23954e15-29e1-49d3-9614-772e33562fc4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2f4910f6-3fdf-4133-b714-b6752d5bdb94": {
      "main": [
        [
          {
            "node": "af2a5b70-b8e7-4ab4-b9cb-29da669d1474",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "46fe1437-f8b0-4ede-9cc6-23958351755a": {
      "main": [
        [
          {
            "node": "1f2dda49-fde7-44f8-8efd-e6ffd73e4e96",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d2232908-5186-4e0a-b771-7230028b8b43": {
      "main": [
        [
          {
            "node": "23954e15-29e1-49d3-9614-772e33562fc4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1920b78c-97ac-4303-a055-e2541cf12f29": {
      "main": [
        [
          {
            "node": "2360ade2-1686-4554-beb3-76ba59e16408",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Preguntas frecuentes

¿Cómo usar este flujo de trabajo?

Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.

¿En qué escenarios es adecuado este flujo de trabajo?

Avanzado

¿Es de pago?

Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.

Información del flujo de trabajo
Nivel de dificultad
Avanzado
Número de nodos18
Categoría-
Tipos de nodos9
Descripción de la dificultad

Adecuado para usuarios avanzados, flujos de trabajo complejos con 16+ nodos

Autor
Adnan Tariq

Adnan Tariq

@adnantariq

Founder of CYBERPULSE AI — helping security teams and SMEs eliminate repetitive tasks through modular n8n automations. I build workflows for vulnerability triage, compliance reporting, threat intel, and Red/Blue/GRC ops. Book a session if you'd like custom automation for your use case. https://linkedin.com/in/adnan-tariq-4b2a1a47

Enlaces externos
Ver en n8n.io

Compartir este flujo de trabajo

Categorías

Categorías: 34