8
n8n 中文网amn8n.com

AI分类处理的安全中心警报

中级

这是一个SecOps, Multimodal AI领域的自动化工作流,包含 12 个节点。主要使用 If, Set, Code, Gmail, Webhook 等节点。 使用GPT-4.1 Mini分类AWS安全配置错误并向Gmail发送警报

前置要求
  • Google 账号和 Gmail API 凭证
  • HTTP Webhook 端点(n8n 会自动生成)
  • Airtable API Key
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "bBRp9TmumfujcoHs",
  "meta": {
    "instanceId": "b9d26382fbc087c17fe86e193d09f32e5d99f4a254d13f3b8ee28079e71766cf",
    "templateCredsSetupCompleted": true
  },
  "name": "AI 分类处理的安全中心警报",
  "tags": [],
  "nodes": [
    {
      "id": "7be0eeba-8700-4b51-a40f-db84c7c533b1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        -272
      ],
      "webhookId": "bebea408-3564-49c8-9407-14a4102fe0cf",
      "parameters": {
        "path": "aws-misconfig",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "lastNode"
      },
      "typeVersion": 2
    },
    {
      "id": "ac0b1a8d-14a0-4e6d-be3a-779a57603869",
      "name": "标准化发现结果",
      "type": "n8n-nodes-base.code",
      "position": [
        672,
        -176
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Code created by ca7ai\n// n8n Code node (Run Once for Each Item)\n// Normalizes Security Hub / AWS Config events whether they arrive at root or under body\nconst evt = $json.body ?? $json;\n\n// Security Hub finding (EventBridge or raw)\nconst sh = evt?.detail?.findings?.[0] || (Array.isArray(evt.findings) ? evt.findings[0] : null);\n// AWS Config notification\nconst cfg = evt?.detail?.configRuleName ? evt.detail : null;\n// Raw Security Hub finding at root\nconst rawSh = (!sh && evt?.ProductArn) ? evt : null;\n\nconst f = sh || rawSh || {};\n\nconst sev   = f?.Severity?.Label || (cfg ? \"MEDIUM\" : \"UNKNOWN\");\nconst title = f?.Title || cfg?.configRuleName || \"Finding\";\nconst desc  = f?.Description || cfg?.newEvaluationResult?.annotation || \"—\";\nconst id    = f?.Id\n  || cfg?.newEvaluationResult?.evaluationResultIdentifier?.evaluationResultQualifier?.configRuleName\n  || String(Date.now());\nconst res   = f?.Resources?.[0]?.Id || cfg?.resourceId || \"unknown\";\nconst types = f?.Types || [];\nconst account = evt?.account || f?.AwsAccountId || \"unknown\";\nconst region  = evt?.region  || f?.Region       || \"unknown\";\n\n// Derive service + hints\nlet service = \"UNKNOWN\";\nif (/^arn:aws:s3:::/.test(res) || types.some(t => t.includes(\"S3\"))) service = \"S3\";\nelse if (types.some(t => /SecurityGroup/i.test(t)) || /sg-/.test(res)) service = \"EC2-SG\";\nelse if (types.some(t => /IAM/i.test(t))) service = \"IAM\";\nelse if (types.some(t => /RDS|SQL|DB/i.test(t))) service = \"RDS\";\nelse if (f?.ProductArn) service = (f.ProductArn.split(\":\")[5] || \"UNKNOWN\");\n\nconst misconfig_hints = [];\nif (service === \"S3\") misconfig_hints.push(\"s3\");\nif (/0\\.0\\.0\\.0\\/0|Public|Open|world/i.test(desc)) misconfig_hints.push(\"public\");\nif (service === \"EC2-SG\") misconfig_hints.push(\"sg\");\nif (service === \"IAM\")    misconfig_hints.push(\"iam\");\nif (service === \"RDS\")    misconfig_hints.push(\"db\");\n\n// IMPORTANT: return a SINGLE object (not an array) in this mode\nreturn {\n  finding_id: id,\n  title,\n  description: desc,\n  severity: sev,\n  resource_id: res,\n  service,\n  account,\n  region,\n  product_types: types,\n  misconfig_hints,\n  raw: evt,\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "be7fa618-9c8c-4418-a405-a4f2787faaf5",
      "name": "发送消息",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1520,
        -176
      ],
      "webhookId": "cfac9d61-e17a-467c-a742-7cb9da960a7f",
      "parameters": {
        "sendTo": "test@gmail.com",
        "message": "=={{ (() => {  const nf = $node[\"Normalize Finding\"].json;  const ai = typeof $node[\"AI Prioritizer\"].json.message.content === 'string'    ? JSON.parse($node[\"AI Prioritizer\"].json.message.content)    : $node[\"AI Prioritizer\"].json.message.content;  const steps = (ai.remediation || []).map(s => `<li>${s}</li>`).join('');  const tags  = (ai.tags || []).join(', ');  const airtableId = $node[\"Airtable - Create Record\"].json?.id || '';  const airtableLine = airtableId ? `<p><b>Airtable Record ID:</b> ${airtableId}</p>` : '';  return `    <h2>AWS Misconfig Alert</h2>    <p><b>Priority:</b> ${ai.priority} &nbsp; <b>Severity:</b> ${nf.severity}</p>    <p><b>Title:</b> ${nf.title}</p>    <p><b>Service:</b> ${nf.service} &nbsp; <b>Resource:</b> ${nf.resource_id}</p>    <p><b>Account:</b> ${nf.account} &nbsp; <b>Region:</b> ${nf.region}</p>    <p><b>Why:</b> ${ai.rationale}</p>    <p><b>Remediation:</b></p>    <ol>${steps}</ol>    <p><b>Tags:</b> ${tags || '—'}</p>    ${airtableLine}    <details><summary>Raw finding</summary>      <pre style=\"background:#f6f8fa;padding:12px;border-radius:6px;white-space:pre-wrap\">${JSON.stringify(nf.raw || nf, null, 2)}</pre>    </details>  `;})() }}",
        "options": {},
        "subject": "=={{ `[${JSON.parse($node[\"AI Prioritizer\"].json.message.content).priority}] ${$node[\"Normalize Finding\"].json.title} — ${$node[\"Normalize Finding\"].json.resource_id} (${ $node[\"Normalize Finding\"].json.service })` }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "3P9F7770YiCuujN",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "c0fb42e3-e670-44f9-a3fa-0b2c3dec2ae1",
      "name": "AI 优先级排序器",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        896,
        -176
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini",
          "cachedResultName": "GPT-4.1-MINI"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are a cloud SecOps triage assistant. Given a normalized AWS finding JSON, return a STRICT JSON object:\n\n{\n  \"priority\": \"P0|P1|P2|P3\",\n  \"rationale\": \"one-paragraph reason referencing severity, resource, and exposure\",\n  \"remediation\": [\"step 1\", \"step 2\", \"...\"],\n  \"tags\": [\"s3\",\"iam\",\"public\", \"...\"]\n}\n\nMapping guidance:\n- Treat publicly accessible data (e.g., public S3 buckets, 0.0.0.0/0 on admin ports, open RDS) as P0 or P1 depending on blast radius.\n- Internal-only or low impact → P2/P3.\n- If severity label is CRITICAL/HIGH, bias to P0/P1.\n\nFinding:\n{{ JSON.stringify($json, null, 2) }}\n"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "openAiApi": {
          "id": "IZ5MCCCsozmZld3",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "42721c67-8c6d-4063-826b-b59e01a3d8ca",
      "name": "Airtable - 创建记录",
      "type": "n8n-nodes-base.airtable",
      "position": [
        1296,
        -176
      ],
      "parameters": {
        "base": {
          "__rl": true,
          "mode": "list",
          "value": "appzIE2wRRYUbvl50",
          "cachedResultUrl": "https://airtable.com/uuuu",
          "cachedResultName": "misconfigs"
        },
        "table": {
          "__rl": true,
          "mode": "list",
          "value": "tblPDewIrVYYYYNyi",
          "cachedResultUrl": "https://airtable.com/uuuu/uuuu",
          "cachedResultName": "finding_table"
        },
        "columns": {
          "value": {
            "id": "={{ $('Normalize Finding').item.json.finding_id }}",
            "Tags": "={{ $json.message.content.tags[0] }}{{ $json.message.content.tags[1] }}{{ $json.message.content.tags[2] }}{{ $json.message.content.tags[3] }}{{ $json.message.content.tags[4] }}",
            "Title": "={{ $('Normalize Finding').item.json.title }}",
            "Region": "={{ $('Normalize Finding').item.json.region }}",
            "Account": "={{ $('Normalize Finding').item.json.account }}",
            "Service": "={{ $('Normalize Finding').item.json.service }}",
            "Priority": "={{ $json.message.content.priority }}",
            "Resource": "={{ $('Normalize Finding').item.json.raw.detail.findings[0].Resources[0].Id }}",
            "Severity": "={{ $('Normalize Finding').item.json.severity }}",
            "Rationale": "={{ $json.message.content.rationale }}",
            "Finding ID": "={{ $('Normalize Finding').item.json.raw.detail.findings[0].Id }}",
            "Remediation": "={{ $json.message.content.remediation[0] }}{{ $json.message.content.remediation[1] }}{{ $json.message.content.remediation[2] }}{{ $json.message.content.remediation[3] }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "id",
              "defaultMatch": true
            },
            {
              "id": "Finding ID",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Finding ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Title",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Severity",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Severity",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Priority",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Priority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Resource",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Resource",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Service",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Service",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Account",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Account",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Region",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Region",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tags",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Tags",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rationale",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Rationale",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Remediation",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "Remediation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "upsert"
      },
      "credentials": {
        "airtableTokenApi": {
          "id": "aftfB5RyLZKQ1111",
          "name": "Airtable Access Token"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "6f9bf9c6-61ee-4b25-844c-5f03c9979f9e",
      "name": "编辑字段",
      "type": "n8n-nodes-base.set",
      "position": [
        1744,
        -176
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{\n  {\n    resp: {\n      status: \"processed\",\n      priority: $node[\"Airtable - Create Record\"].json.fields.Priority,\n      finding_id: $node[\"Normalize Finding\"].json.finding_id\n    }\n  }\n}}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "fbb5d2af-7eb2-414f-b79e-2a51ddb9b21f",
      "name": "SNS 处理器",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        -272
      ],
      "parameters": {
        "jsCode": "const b = $json.body ?? $json;\nconst token = $json.query?.token ?? b.token;\nif (token !== 'MY_SUPER_TOKEN') throw new Error('unauthorized');\n\nif (b.Type === 'SubscriptionConfirmation' && b.SubscribeURL) {\n  return { mode: 'confirm', subscribeUrl: b.SubscribeURL };\n}\n\nlet event = b;\nif (b.Type === 'Notification' && b.Message) {\n  try { event = JSON.parse(b.Message); } catch {}\n}\nreturn { mode: 'notify', event };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b61e5e70-697d-4fe4-9897-9a116aa5aff1",
      "name": "条件判断",
      "type": "n8n-nodes-base.if",
      "position": [
        448,
        -272
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d192995f-8809-4b60-8f6a-b7bd2e3e47b0",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "=={{ $json.mode === 'confirm' }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5c1145c7-6dd6-47e3-9fee-e1fb15318a22",
      "name": "SNS 确认",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        672,
        -368
      ],
      "parameters": {
        "url": "=={{ $json.subscribeUrl }}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "831507c3-b00f-44ec-8506-c0310904eb6e",
      "name": "编辑字段1",
      "type": "n8n-nodes-base.set",
      "position": [
        984,
        -368
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "346708e7-629e-4d8a-8a98-994b9526b55d",
              "name": "resp",
              "type": "object",
              "value": "=resp = { status: \"subscribed\", statusCode: $json.statusCode || 200 }"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b39a925c-911d-4e61-a0e6-5aadc4ccfd38",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        -32
      ],
      "parameters": {
        "width": 336,
        "content": "## 注意"
      },
      "typeVersion": 1
    },
    {
      "id": "1bea05cd-6468-4c74-aeec-8c60d69411c4",
      "name": "便签 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -464,
        -368
      ],
      "parameters": {
        "color": 4,
        "width": 336,
        "height": 304,
        "content": "## 标题:工作原理(连接)"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "044dc52b-81c7-43ab-a1c2-5be287a0d970",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "SNS Confirm",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Normalize Finding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "SNS Handler",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SNS Confirm": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SNS Handler": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        []
      ]
    },
    "AI Prioritizer": {
      "main": [
        [
          {
            "node": "Airtable - Create Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Finding": {
      "main": [
        [
          {
            "node": "AI Prioritizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Airtable - Create Record": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级 - 安全运维, 多模态 AI

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量12
分类2
节点类型9
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

作者
Calistus Christian

Calistus Christian

@ca7ai

Automation strategist and AI workflow architect creating intelligent, agent-driven processes that streamline operations. I build advanced n8n solutions integrating AI, cloud services, and real-time data orchestration for scalable impact. Explore my free workflows to get started. Premium, custom-built solutions are also available. Connect with me on LinkedIn for tailored automation expertise.

外部链接
在 n8n.io 查看

分享此工作流