{
  "id": "v6SbteXL5dW8fGKQ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automated Domain & IP Reputation Guard with HashiCorp Vault",
  "tags": [
    {
      "id": "Sp23rEbQB2Jd4jXe",
      "name": "ThreatFox",
      "createdAt": "2026-03-13T13:39:00.381Z",
      "updatedAt": "2026-03-13T13:39:00.381Z"
    },
    {
      "id": "TIbt5FCaXtha7wlD",
      "name": "VirusTotal",
      "createdAt": "2026-03-13T13:38:34.538Z",
      "updatedAt": "2026-03-13T13:38:34.538Z"
    },
    {
      "id": "bCcoQW7lpKvNTcSS",
      "name": "Abuse.ch",
      "createdAt": "2026-03-13T13:38:42.812Z",
      "updatedAt": "2026-03-13T13:38:42.812Z"
    },
    {
      "id": "myyAYIm8lG8YODFq",
      "name": "HashiCorpVault",
      "createdAt": "2026-03-13T13:38:26.975Z",
      "updatedAt": "2026-03-13T13:38:26.975Z"
    }
  ],
  "nodes": [
    {
      "id": "84dca179-a0f0-49fd-8e5b-5feb60b26e82",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3104,
        -496
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 3
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "8bf1829c-9df2-41b2-a86e-75cc263170bb",
      "name": "Select rows from a table",
      "type": "n8n-nodes-base.mySql",
      "position": [
        -2592,
        -496
      ],
      "parameters": {
        "limit": 1,
        "table": {
          "__rl": true,
          "mode": "name",
          "value": "cyber_intelligence.v_pending_analysis"
        },
        "options": {},
        "operation": "select"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "ff297aa5-0e9f-4f5e-b2e1-7a7eeaf96bf8",
      "name": "VirusTotal IP Scan",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2160,
        -176
      ],
      "parameters": {
        "": "",
        "url": "=https://www.virustotal.com/api/v3/ip_addresses/{{ $('Select rows from a table').item.json.observable_ip }}",
        "method": "GET",
        "options": {},
        "sendBody": false,
        "sendQuery": false,
        "curlImport": "",
        "infoMessage": "",
        "sendHeaders": false,
        "authentication": "predefinedCredentialType",
        "httpVariantWarning": "",
        "nodeCredentialType": "virusTotalApi",
        "provideSslCertificates": false
      },
      "credentials": {
        "virusTotalApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4,
      "extendsCredential": "virusTotalApi"
    },
    {
      "id": "301ce32c-69d1-4561-809b-792422ada724",
      "name": "If 'malicious' or 'suspicious'",
      "type": "n8n-nodes-base.if",
      "position": [
        -1968,
        -176
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "f1809d7a-923a-4ef7-b24f-fcbf6b5cb41d",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.data.attributes.last_analysis_stats.malicious }}",
              "rightValue": 0
            },
            {
              "id": "ca056a70-f3eb-4aa5-bae9-05bd477cf4ce",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.data.attributes.last_analysis_stats.suspicious }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "a56ac689-7f44-4614-bf7e-940d85ce6c40",
      "name": "Virus total API TOKEN",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -2352,
        -176
      ],
      "parameters": {
        "secretPath": "cyber-sentinel/api-keys/virustotal",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2d61d46a-cd6d-4b12-a052-6cca8f988d34",
      "name": "Gemini AI Studio",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -736,
        -784
      ],
      "parameters": {
        "secretPath": "cyber-sentinel/api-keys/gemini/home-network-guardian",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a532302f-b601-47da-9197-9910b6c7b6df",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -832,
        -544
      ],
      "parameters": {
        "text": "=ROLE:\nYou are a Senior Cyber Threat Intelligence Analyst in the Cyber Sentinel system.\nYour task is to evaluate an IP or FQDN artifact based on aggregated data from\nVirusTotal (primary), ThreatFox (primary), and URLhaus (supporting context only),\nand return a structured analysis.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nSCORING POLICY (Detection-First, Source-Weighted Model)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nThe threat scale (1-5) and recommended actions are NOT hardcoded in this prompt.\nThey are loaded dynamically from the database table `dic_threat_levels` and\nprovided to you in the variable {{ $json.threat_scale }} at runtime.\n\nYou MUST use the descriptions and action_recommended values from that table as\nthe authoritative source of truth. This prompt only defines HOW to map evidence\nto a score \u2014 the meaning of each score comes from the database.\n\nTHREAT SCALE (loaded from DB):\n{{ $json.threat_scale }}\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nSOURCE WEIGHTING (CRITICAL \u2014 read carefully)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nThis system evaluates IP addresses and FQDNs, NOT individual URLs. Therefore:\n\nPRIMARY SOURCES (drive the score):\n  \u2022 VirusTotal \u2014 reputation of the IP/FQDN itself\n  \u2022 ThreatFox  \u2014 known malicious infrastructure (C2, payload delivery hosts)\n\nSUPPORTING SOURCE (context only, max +1 modifier):\n  \u2022 URLhaus \u2014 historical URLs hosted on this IP/FQDN\n\nURLhaus RULE (IMPORTANT):\nURLhaus reports URLs that were once hosted on a given host. Large legitimate\nplatforms (bitbucket.org, github.com, pastebin.com, discord cdn, dropbox, etc.)\nwill ALWAYS have many URLhaus hits because attackers abuse them \u2014 but the\nplatforms themselves are not malicious.\n\nTherefore URLhaus alone NEVER raises the score. URLhaus may add at most +1 to\nthe score IF AND ONLY IF:\n  (a) VirusTotal OR ThreatFox already flagged the artifact as suspicious, AND\n  (b) URLhaus reports `is_active_threat: true` (currently online payload).\n\nIf URLhaus is the ONLY source with hits \u2192 ignore it for scoring purposes,\nbut mention it in `analysis_pl` as context.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nSCORING DECISION LOGIC\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nStep 1 \u2014 Evaluate VirusTotal (PRIMARY):\n  \u2022 no_data: true OR vt_malicious_count = 0       \u2192 VT contributes: CLEAN\n  \u2022 vt_is_big_player = true AND vt_malicious_count < 3 \u2192 VT contributes: CLEAN\n    (trusted infra like Google/MS/Cloudflare with minor noise)\n  \u2022 vt_malicious_count = 1-3                       \u2192 VT contributes: LOW\n  \u2022 vt_malicious_count = 4-9                       \u2192 VT contributes: MEDIUM\n  \u2022 vt_malicious_count >= 10                       \u2192 VT contributes: HIGH\n\nStep 2 \u2014 Evaluate ThreatFox (PRIMARY):\n  \u2022 no_data: true                                  \u2192 TF contributes: CLEAN\n  \u2022 Listed but threatfox_active = false            \u2192 TF contributes: MEDIUM\n  \u2022 threatfox_active = true                        \u2192 TF contributes: HIGH\n    (active C2 / known malware family hit = critical signal)\n\nStep 3 \u2014 Combine PRIMARY sources into base score:\n  \u2022 Both CLEAN                                     \u2192 base = 1\n  \u2022 One LOW, other CLEAN                           \u2192 base = 2\n  \u2022 One MEDIUM, other CLEAN                        \u2192 base = 3\n  \u2022 Both have hits (any level)                     \u2192 base = 4\n  \u2022 Either source = HIGH                           \u2192 base = 4\n  \u2022 TF active=true AND VT >= 4 detections          \u2192 base = 5\n  \u2022 TF active=true AND identified malware family   \u2192 base = 5\n\nStep 4 \u2014 Apply URLhaus modifier (supporting):\n  \u2022 If base >= 3 AND urlhaus.is_active_threat = true \u2192 base + 1 (capped at 5)\n  \u2022 Otherwise: no change\n\nStep 5 \u2014 Apply Big Player override:\n  \u2022 If vt_is_big_player = true AND no malware family confirmed by ThreatFox\n    \u2192 cap final score at 2 (these are almost always false positives or\n      abuse of legitimate infrastructure, not infrastructure compromise)\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nINPUT DATA\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n{{ JSON.stringify($('Code for Merge').item.json) }} \n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nOUTPUT FORMAT (STRICT JSON ONLY)\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n{\n  \"threat_score\": <integer 1-5>,\n  \"is_malicious\": <boolean \u2014 true if score >= 4>,\n  \"threat_label\": \"<one of: Clean | Suspicious | Phishing | Malware | Botnet | C2>\",\n  \"verdict_en\": \"<single-line technical summary, max 200 chars>\",\n  \"analysis_pl\": \"<single-line Polish commentary for Lukasz covering: 1) Wlasciciel IP/Hostingu, 2) Typ zagrozenia i rodziny malware (jesli wykryte), 3) Rekomendacja zgodna z action_recommended ze skali w bazie, 4) Uwagi o URLhaus jesli istotne>\",\n  \"active_providers\": [<list of source names that contributed evidence: \"virustotal\", \"threatfox\", \"urlhaus\">],\n  \"scoring_rationale\": \"<single-line explanation of WHY this score, referencing the decision steps \u2014 useful for the future self-healing agent>\"\n}\n\nFORMATTING RULES:\n1. All string values MUST be single-line. Never use \\n inside strings.\n2. SQL ESCAPING: replace every single quote (') with two single quotes ('').\n3. Do not use backslashes or backticks anywhere in string values.\n4. `active_providers` must list ONLY sources that returned no_data=false.\n5. `scoring_rationale` is mandatory \u2014 it will be used by future workflows to\n   audit and improve this scoring logic.",
        "options": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "executeOnce": true,
      "typeVersion": 3.1
    },
    {
      "id": "db7320d8-0d9f-4035-83ac-60eae5e0659a",
      "name": "MySQL Pass",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -2944,
        -496
      ],
      "parameters": {
        "secretPath": "=cyber-sentinel/credentials/mysql/app_manager",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fcc4211c-8807-4aa5-adc5-3abf05716f1d",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -960,
        -368
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "583369e8-7519-42a2-9174-0b1b6a811372",
      "name": "Parse AI Agent output",
      "type": "n8n-nodes-base.code",
      "position": [
        -224,
        -720
      ],
      "parameters": {
        "jsCode": "let rawText = $json.output;\nlet cleanJson = rawText.replace(/```json\\n|```/g, \"\").trim();\ncleanJson = cleanJson.replace(/[\\r\\n]+/g, \" \");\n\ntry {\n  const data = JSON.parse(cleanJson);\n  const providerDetails = [];\n\n  // ULEPSZONA Funkcja pomocnicza\n  const getMongoId = (nodeName, providerKey) => {\n    try {\n      // Pobieramy wszystkie dane z danego w\u0119z\u0142a, nie tylko ostatni element\n      const nodeData = $(nodeName).all(); \n      \n      for (const entry of nodeData) {\n        if (entry.json && entry.json[providerKey] && entry.json[providerKey].mongo_id) {\n          return entry.json[providerKey].mongo_id;\n        }\n      }\n    } catch (e) {\n      return null;\n    }\n    return null;\n  };\n\n  // Logika sprawdzania dostawc\u00f3w pozostaje bez zmian, ale funkcja powy\u017cej jest skuteczniejsza\n  if (data.active_providers.includes(\"virustotal\")) {\n    const vt_id = getMongoId('Data reduction and aggregation - VirusTotal', 'virustotal');\n    if (vt_id) providerDetails.push({ name: \"VirusTotal\", mongo_id: vt_id });\n  }\n\n  if (data.active_providers.includes(\"threatfox\")) {\n    const tf_id = getMongoId('Data reduction and aggregation - ThreatFox', 'threatfox');\n    if (tf_id) providerDetails.push({ name: \"Abuse_ThreatFox\", mongo_id: tf_id });\n  }\n\n  if (data.active_providers.includes(\"urlhaus\")) {\n    const uh_id = getMongoId('Data reduction and aggregation - Urlhaus', 'urlhaus');\n    if (uh_id) providerDetails.push({ name: \"Abuse_URLhaus\", mongo_id: uh_id });\n  }\n\n  return {\n    score: data.threat_score,\n    verdict_en: data.verdict_en,\n    analysis_pl: data.analysis_pl,\n    provider_details: providerDetails\n  };\n\n} catch (error) {\n  return { error: \"Parsing failed\", details: error.message, raw_received: rawText };\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "ce9c8563-971f-4531-a990-ecf3cd0024ab",
      "name": "Insert AI verdict",
      "type": "n8n-nodes-base.mySql",
      "position": [
        0,
        -720
      ],
      "parameters": {
        "query": "-- KROK 1: Wstawienie unikalnego werdyktu do nowej tabeli s\u0142ownikowej\nINSERT INTO cyber_intelligence.ai_analysis_results (\n    threat_score, \n    verdict_summary_en, \n    analysis_pl\n) VALUES (\n    {{ $json.score }},\n    '{{ $json.verdict_en }}',\n    '{{ $json.analysis_pl }}'\n);\n\n-- Zapami\u0119tujemy ID werdyktu\nSET @last_result_id = LAST_INSERT_ID();\n\n-- KROK 2: Wstawienie rekordu zdarzenia do threat_indicators\nINSERT INTO cyber_intelligence.threat_indicators (\n    dns_query_id,\n    type_id,\n    analysis_result_id,\n    last_scan\n) VALUES (\n    {{ $('Select rows from a table').item.json.dns_query_id }}, \n    (SELECT id FROM cyber_intelligence.dic_indicator_types WHERE name = 'IP'),\n    @last_result_id,\n    NOW()\n);\n\n-- Zapami\u0119tujemy ID zdarzenia (do powi\u0105zania ze skanerami)\nSET @last_event_id = LAST_INSERT_ID();\n\n-- KROK 3: Dynamiczne wstawienie szczeg\u00f3\u0142\u00f3w skaner\u00f3w (VirusTotal, ThreatFox itp.)\n{{ $json.provider_details.map(p => `\nINSERT INTO cyber_intelligence.threat_indicator_details (indicator_id, source_id, mongo_ref_id)\nSELECT @last_event_id, id, '${p.mongo_id}' \nFROM cyber_intelligence.dic_source_providers WHERE name = '${p.name}';\n`).join('\\n') }}\n\n-- Zwracamy ID zdarzenia dla kolejnych krok\u00f3w (np. do maila)\nSELECT @last_event_id AS indicator_id;",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5,
      "alwaysOutputData": true
    },
    {
      "id": "adabcf0d-8191-4bf2-ba73-07e78d78006f",
      "name": "Send an Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        0,
        -496
      ],
      "parameters": {
        "html": "=<!DOCTYPE html>\n<html>\n<head>\n    <style>\n        body { font-family: 'Segoe UI', Tahoma, sans-serif; background-color: #121212; color: #e0e0e0; margin: 0; padding: 20px; }\n        .card { max-width: 600px; margin: auto; background: #1e1e1e; border: 1px solid #333; border-radius: 8px; overflow: hidden; }\n        .header { padding: 15px; text-align: center; }\n        .content { padding: 25px; }\n        .score-box { background: #2d2d2d; border-radius: 5px; padding: 10px; text-align: center; margin-bottom: 20px; border: 1px solid #444; }\n        .ip-addr { font-family: 'Courier New', monospace; color: #64ffda; font-size: 18px; font-weight: bold; }\n        .analysis { background: #252525; padding: 15px; border-radius: 4px; font-size: 14px; line-height: 1.6; margin-top: 15px; }\n        .provider-tag { display: inline-block; background: #37474f; color: #cfd8dc; padding: 2px 8px; border-radius: 3px; font-size: 11px; margin-right: 5px; border: 1px solid #546e7a; }\n        .btn { display: inline-block; padding: 10px 20px; color: white; text-decoration: none; border-radius: 4px; font-weight: bold; margin-top: 20px; font-size: 14px; }\n        .footer { text-align: center; font-size: 11px; color: #777; padding: 15px; }\n    </style>\n</head>\n<body>\n    <div class=\"card\" style=\"border-top: 4px solid {{ ({1:'#4caf50',2:'#4caf50',3:'#ffb300',4:'#f44336',5:'#f44336'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n        <div class=\"header\" style=\"background: {{ ({1:'#1b3320',2:'#1b3320',3:'#332a13',4:'#3a1f1f',5:'#3a1f1f'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n            <h2 style=\"margin:0; color: {{ ({1:'#81c784',2:'#81c784',3:'#ffd54f',4:'#ff5252',5:'#ff5252'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n                {{ ({1:'\u2705 INFO',2:'\u2705 INFO',3:'\u26a0\ufe0f REVIEW',4:'\ud83d\udea8 ALERT',5:'\ud83d\udea8 ALERT'})[$node[\"Parse AI Agent output\"].json.score] }}: Cyber Sentinel\n            </h2>\n        </div>\n        <div class=\"content\">\n            <div class=\"score-box\">\n                <div style=\"font-size: 12px; text-transform: uppercase; color: #888; letter-spacing: 1px;\">Threat Severity Score</div>\n                <div style=\"font-size: 36px; font-weight: bold; color: {{ ({1:'#81c784',2:'#81c784',3:'#ffd54f',4:'#ff5252',5:'#ff5252'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n                    {{ $node[\"Parse AI Agent output\"].json.score }}/5\n                </div>\n                <div style=\"font-size: 12px; color: #aaa; margin-top: 4px; text-transform: uppercase; letter-spacing: 0.5px;\">\n                    {{ ({1:'Clean / Allow',2:'Low Risk / Monitor',3:'Suspicious / Review',4:'Malicious / Block',5:'Critical / Block + Alert'})[$node[\"Parse AI Agent output\"].json.score] }}\n                </div>\n            </div>\n            <p><strong>Obiekt (IP):</strong> <span class=\"ip-addr\">{{ $('Select rows from a table').item.json.observable_ip }}</span></p>\n            <p><strong>Powi\u0105zana domena:</strong> <span style=\"color: #90caf9;\">{{ $('Select rows from a table').item.json.fqdn }}</span></p>\n\n            <div style=\"margin-top: 10px;\">\n                <strong style=\"font-size: 13px; color: #888;\">Aktywne \u017ar\u00f3d\u0142a danych:</strong><br>\n                {{ $node[\"Parse AI Agent output\"].json.provider_details.map(p => `<span class=\"provider-tag\">${p.name}</span>`).join('') }}\n            </div>\n            <div class=\"analysis\" style=\"border-left: 4px solid {{ ({1:'#4caf50',2:'#4caf50',3:'#ffb300',4:'#f44336',5:'#f44336'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n                <strong style=\"color: {{ ({1:'#81c784',2:'#81c784',3:'#ffd54f',4:'#ff5252',5:'#ff5252'})[$node[\"Parse AI Agent output\"].json.score] }};\">Analiza (PL):</strong><br>\n                <div style=\"margin-top: 5px;\">\n                    {{ $node[\"Parse AI Agent output\"].json.analysis_pl }}\n                </div>\n            </div>\n            <p style=\"color: #bbb; font-style: italic; font-size: 12px; margin-top: 15px; border-top: 1px solid #333; padding-top: 10px;\">\n                <strong>Technical Verdict:</strong> {{ $node[\"Parse AI Agent output\"].json.verdict_en }}\n            </p>\n            <div style=\"text-align: center;\">\n                <a href=\"https://rdap.arin.net/registry/ip/{{ $('Select rows from a table').item.json.observable_ip }}\" class=\"btn\" style=\"background-color: {{ ({1:'#4caf50',2:'#4caf50',3:'#ffb300',4:'#f44336',5:'#f44336'})[$node[\"Parse AI Agent output\"].json.score] }};\">\n                    Sprawd\u017a detale IP (RDAP)\n                </a>\n            </div>\n        </div>\n        <div class=\"footer\">\n            System: <strong>Cyber Sentinel v1.0</strong><br>\n            Data: {{ new Date().toLocaleString('pl-PL') }}\n        </div>\n    </div>\n</body>\n</html>",
        "options": {},
        "subject": "=Raport wygenerowany przez system Cyber Sentinel | Data: {{ new Date().toLocaleString('pl-PL') }}",
        "toEmail": "home-network-guardian@proton.me",
        "fromEmail": "={{ $json.data.user }}"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "643dbbfa-6be9-475d-862a-8ba4f895d136",
      "name": "Abuse.CH_ThreatFox request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2160,
        -528
      ],
      "parameters": {
        "url": "https://threatfox-api.abuse.ch/api/v1/",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "search_ioc"
            },
            {
              "name": "search_term",
              "value": "={{ $('Select rows from a table').item.json.fqdn }}"
            },
            {
              "name": "exact_match",
              "value": "true"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "990717f4-746b-423a-a67c-1876f3cb4222",
      "name": "Abuse API TOKEN",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -2352,
        -720
      ],
      "parameters": {
        "secretPath": "cyber-sentinel/api-keys/abuse/api-key",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3ca870ed-b0c3-407c-8cf3-66d54593ae62",
      "name": "Get Email Pass",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -224,
        -496
      ],
      "parameters": {
        "secretPath": "cyber-sentinel/credentials/gmail/l94524506",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0a02d8d8-5055-44a4-b165-848eb61e07e7",
      "name": "Abuse.CH_URLHaus",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2144,
        -912
      ],
      "parameters": {
        "url": "https://urlhaus-api.abuse.ch/v1/host/",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "host",
              "value": "={{ $('Select rows from a table').item.json.fqdn }}"
            }
          ]
        },
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "392f063c-7556-40d7-91cc-045f62268f1f",
      "name": "Insert doc URLHaus",
      "type": "n8n-nodes-base.mongoDb",
      "position": [
        -1568,
        -1008
      ],
      "parameters": {
        "fields": "=resource,type,source_provider,scan_date,raw_data",
        "options": {
          "dateFields": "",
          "useDotNotation": false
        },
        "operation": "insert",
        "collection": "threat_data_raw"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2,
      "alwaysOutputData": false
    },
    {
      "id": "f941de13-8d24-4fd8-91cd-62921d64f886",
      "name": "Edit Json for Mongo - URLHaus",
      "type": "n8n-nodes-base.code",
      "position": [
        -1744,
        -1008
      ],
      "parameters": {
        "jsCode": "return {\n  resource: $('Select rows from a table').first().json.observable_ip,\n  type: 'FQDN',\n  source_provider: 'Abuse_URLhaus', \n  scan_date: new Date().toISOString(),\n  raw_data: $('Abuse.CH_URLHaus').item.json\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "61246457-db7e-4a4a-9252-c1490d95efc5",
      "name": "Insert a clear scan - URLHaus",
      "type": "n8n-nodes-base.mySql",
      "position": [
        -1744,
        -800
      ],
      "parameters": {
        "query": "-- 1. Wstawiamy bazowy wynik \"Clean\" do nowej tabeli wynik\u00f3w\nINSERT INTO cyber_intelligence.ai_analysis_results (\n    threat_score, \n    verdict_summary_en, \n    analysis_pl\n) VALUES (\n    1,\n    null,\n    null\n);\n\n-- 2. Pobieramy ID tego wyniku\nSET @last_clean_result_id = LAST_INSERT_ID();\n\n-- 3. Wstawiamy rekord do g\u0142\u00f3wnej tabeli zdarze\u0144 (ju\u017c bez source_id!)\nINSERT INTO cyber_intelligence.threat_indicators (\n    dns_query_id,\n    type_id,\n    analysis_result_id,\n    last_scan\n) VALUES (\n    {{ $('Select rows from a table').item.json.dns_query_id }}, \n    (SELECT id FROM cyber_intelligence.dic_indicator_types WHERE name = 'FQDN'),\n    @last_clean_result_id,\n    NOW()\n);\n\n-- 4. Rejestrujemy, kt\u00f3ry skaner to zaraportowa\u0142 w tabeli szczeg\u00f3\u0142\u00f3w\nINSERT INTO cyber_intelligence.threat_indicator_details (\n    indicator_id, \n    source_id, \n    mongo_ref_id\n) VALUES (\n    LAST_INSERT_ID(),\n    (SELECT id FROM cyber_intelligence.dic_source_providers WHERE name = 'Abuse_URLhaus'),\n    'NO_DATA'\n);",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "3052da02-3e4f-4296-acdb-5046b1c096a0",
      "name": "Edit Json for Mongo - ThreatFox",
      "type": "n8n-nodes-base.code",
      "position": [
        -1744,
        -608
      ],
      "parameters": {
        "jsCode": "return {\n  resource: $('Select rows from a table').first().json.observable_ip,\n  type: 'FQDN',\n  source_provider: 'Abuse_ThreatFox', \n  scan_date: new Date().toISOString(),\n  raw_data: $('Abuse.CH_ThreatFox request').item.json\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "6d692e11-3337-4359-b4c2-c8240c435155",
      "name": "Insert a clear scan - ThreatFox",
      "type": "n8n-nodes-base.mySql",
      "position": [
        -1744,
        -432
      ],
      "parameters": {
        "query": "-- 1. Wstawienie bazowego werdyktu \"Safe\" do nowej tabeli wynik\u00f3w\n-- Robimy to, aby zachowa\u0107 sp\u00f3jno\u015b\u0107 relacji (analysis_result_id)\nINSERT INTO cyber_intelligence.ai_analysis_results (\n    threat_score, \n    verdict_summary_en, \n    analysis_pl\n) VALUES (\n    1,\n    null,\n    null\n);\n\n-- Zapami\u0119tujemy ID tego wyniku\nSET @last_clean_res_id = LAST_INSERT_ID();\n\n-- 2. Wstawienie rekordu zdarzenia do threat_indicators\nINSERT INTO cyber_intelligence.threat_indicators (\n    dns_query_id,\n    type_id,\n    analysis_result_id,\n    last_scan\n) VALUES (\n    {{ $('Select rows from a table').item.json.dns_query_id }}, \n    (SELECT id FROM cyber_intelligence.dic_indicator_types WHERE name = 'IP'),\n    @last_clean_res_id,\n    NOW()\n);\n\n-- Zapami\u0119tujemy ID zdarzenia (indicator_id)\nSET @last_event_id = LAST_INSERT_ID();\n\n-- 3. Rejestracja konkretnego skanera w tabeli szczeg\u00f3\u0142\u00f3w\nINSERT INTO cyber_intelligence.threat_indicator_details (\n    indicator_id, \n    source_id, \n    mongo_ref_id\n) VALUES (\n    @last_event_id,\n    (SELECT id FROM cyber_intelligence.dic_source_providers WHERE name = 'Abuse_ThreatFox'),\n    '{{ $json.mongo_id || \"NO_DATA\" }}' -- U\u017cywa mongo_id je\u015bli istnieje, inaczej \"NO_DATA\"\n);",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "fc72827d-518f-4148-827b-738fbe1149a7",
      "name": "Insert doc ThreatFox",
      "type": "n8n-nodes-base.mongoDb",
      "position": [
        -1568,
        -608
      ],
      "parameters": {
        "fields": "=resource,type,source_provider,scan_date,raw_data",
        "options": {
          "dateFields": "",
          "useDotNotation": false
        },
        "operation": "insert",
        "collection": "threat_data_raw"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2,
      "alwaysOutputData": false
    },
    {
      "id": "450424c2-bdcd-45a5-b36c-be2a9d646ee4",
      "name": "Insert a clear scan - VirusTotal",
      "type": "n8n-nodes-base.mySql",
      "position": [
        -1744,
        -48
      ],
      "parameters": {
        "query": "-- 1. Wstawienie neutralnego werdyktu do nowej tabeli wynik\u00f3w\nINSERT INTO cyber_intelligence.ai_analysis_results (\n    threat_score, \n    verdict_summary_en, \n    analysis_pl\n) VALUES (\n    1,\n    null,\n    null\n);\n\n-- Zapami\u0119tujemy ID werdyktu\nSET @last_vt_clean_res_id = LAST_INSERT_ID();\n\n-- 2. Wstawienie rekordu do g\u0142\u00f3wnej tabeli zdarze\u0144 (rejestr skanowania)\nINSERT INTO cyber_intelligence.threat_indicators (\n    dns_query_id,\n    type_id,\n    analysis_result_id,\n    last_scan\n) VALUES (\n    {{ $('Select rows from a table').item.json.dns_query_id }}, \n    (SELECT id FROM cyber_intelligence.dic_indicator_types WHERE name = 'IP'),\n    @last_vt_clean_res_id,\n    NOW()\n);\n\n-- Zapami\u0119tujemy ID zdarzenia (indicator_id)\nSET @last_vt_event_id = LAST_INSERT_ID();\n\n-- 3. Powi\u0105zanie skanera VirusTotal w tabeli szczeg\u00f3\u0142\u00f3w\nINSERT INTO cyber_intelligence.threat_indicator_details (\n    indicator_id, \n    source_id, \n    mongo_ref_id\n) VALUES (\n    @last_vt_event_id,\n    (SELECT id FROM cyber_intelligence.dic_source_providers WHERE name = 'VirusTotal'),\n    '{{ $json.mongo_id || \"NO_DATA\" }}'\n);",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "d37f9a57-2001-4922-b76b-2ac3f08b851a",
      "name": "Edit Json for Mongo - VirusTotal",
      "type": "n8n-nodes-base.code",
      "position": [
        -1744,
        -240
      ],
      "parameters": {
        "jsCode": "return {\n  resource: $('If \\'malicious\\' or \\'suspicious\\'').item.json.data.id, \n  type: \"IP\",\n  source_provider: \"VirusTotal\", \n  scan_date: new Date().toISOString(),\n  raw_data: $('VirusTotal IP Scan').item.json.data \n};"
      },
      "typeVersion": 2
    },
    {
      "id": "906ced4e-8588-4a7e-8367-d7e4a3735e87",
      "name": "Mongo DB Pass",
      "type": "n8n-nodes-hashi-vault.hashiCorpVault",
      "position": [
        -2768,
        -496
      ],
      "parameters": {
        "secretPath": "cyber-sentinel/credentials/mongodb/admin",
        "additionalFields": {}
      },
      "credentials": {
        "hashiCorpVaultApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "dba05af7-2214-46f8-a689-9f3535e89995",
      "name": "Insert doc VirusTotal",
      "type": "n8n-nodes-base.mongoDb",
      "position": [
        -1568,
        -240
      ],
      "parameters": {
        "fields": "=resource,type,source_provider,scan_date,raw_data",
        "options": {
          "dateFields": "",
          "useDotNotation": false
        },
        "operation": "insert",
        "collection": "threat_data_raw"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2,
      "alwaysOutputData": false
    },
    {
      "id": "f5f11f7c-b94b-4b92-a8d9-8a49fdd3c756",
      "name": "Data reduction and aggregation - Urlhaus",
      "type": "n8n-nodes-base.code",
      "position": [
        -1376,
        -848
      ],
      "parameters": {
        "jsCode": "const item = $input.item.json;\nconst raw = item.raw_data || {};\nconst urls = raw.urls || [];\nconst mongoId = item.id || null;\n\nconst onlineCount = urls.filter(u => u.url_status === 'online').length;\n\nconst tagMap = {};\nurls.flatMap(u => u.tags || []).forEach(tag => {\n    const cleanTag = tag.trim().toLowerCase();\n    tagMap[cleanTag] = (tagMap[cleanTag] || 0) + 1;\n});\n\nconst topTags = Object.entries(tagMap)\n    .sort((a, b) => b[1] - a[1])\n    .slice(0, 5)\n    .map(entry => `${entry[0]} (${entry[1]}x)`)\n    .join(', ');\n\nconst criticalKeywords = ['c2', 'cobaltstrike', 'rat', 'ransomware', 'brute', 'infostealer', 'stealer', 'brat'];\nconst foundCritical = Object.keys(tagMap).filter(tag => criticalKeywords.includes(tag));\n\nconst urlhausRef = raw.urlhaus_reference || \"No reference link available\";\n\nlet report = `SOURCE: Abuse_URLhaus\\n`;\nif (urls.length === 0 && !raw.urlhaus_reference) {\n    report += \"STATUS: No data available from this source.\";\n} else {\n    report += `- Total URLs in DB: ${raw.url_count || 0}\\n`;\n    report += `- Active (Online) URLs: ${onlineCount} / ${urls.length} (analyzed sample)\\n`;\n    report += `- Top Tags: ${topTags || 'None'}\\n`;\n    report += `- Reference: ${urlhausRef}\\n`; // Dodany atrybut do tekstu\n    \n    if (foundCritical.length > 0) {\n        report += `- CRITICAL FLAGS DETECTED: ${foundCritical.join(', ').toUpperCase()}\\n`;\n    }\n}\n\nif (urls.length === 0 && !raw.urlhaus_reference) {\n    return {\n        \"urlhaus\": {\n            \"no_data\": true,\n            \"urlhaus_report\": \"SOURCE: Abuse_URLhaus\\nSTATUS: No data available.\"\n        }\n    };\n}\n\nreturn {\n    \"urlhaus\": {\n        \"no_data\": false,\n        \"urlhaus_report\": report,\n        \"is_active_threat\": onlineCount > 0,\n        \"urlhaus_reference\": urlhausRef,\n        \"mongo_id\": mongoId\n    }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "e08faa3a-62a3-4237-8656-a0f54c7889d3",
      "name": "Data reduction and aggregation - ThreatFox",
      "type": "n8n-nodes-base.code",
      "position": [
        -1376,
        -608
      ],
      "parameters": {
        "jsCode": "const item = $input.item.json;\nconst raw = item.raw_data || {};\nconst dataList = raw.data || [];\nconst mongoId = item.id || null;\n\nconst malwareFamilies = [...new Set(dataList.map(d => d.malware_printable).filter(m => m))];\nconst threatTypes = [...new Set(dataList.map(d => d.threat_type).filter(t => t))];\n\nconst confidenceScores = dataList.map(d => d.confidence_level).filter(c => c !== undefined);\nconst avgConfidence = confidenceScores.length > 0 \n    ? (confidenceScores.reduce((a, b) => a + b, 0) / confidenceScores.length).toFixed(0) \n    : 0;\n\nconst tagMap = {};\ndataList.flatMap(d => d.tags || []).forEach(tag => {\n    const cleanTag = tag.trim();\n    tagMap[cleanTag] = (tagMap[cleanTag] || 0) + 1;\n});\nconst topTags = Object.entries(tagMap)\n    .sort((a, b) => b[1] - a[1])\n    .slice(0, 5)\n    .map(e => e[0])\n    .join(', ');\n\nconst externalRef = dataList.length > 0 ? dataList[0].reference : \"No reference available\";\n\nlet report = `SOURCE: Abuse_ThreatFox\\n`;\nif (dataList.length === 0 || raw.query_status !== \"ok\") {\n    report += \"STATUS: No indicators found for this resource.\";\n} else {\n    report += `- Detections Found: ${dataList.length}\\n`;\n    report += `- Malware Families: ${malwareFamilies.join(', ') || 'Unknown'}\\n`;\n    report += `- Threat Types: ${threatTypes.join(', ') || 'Unknown'}\\n`;\n    report += `- Average Confidence: ${avgConfidence}%\\n`;\n    report += `- Top Tags: ${topTags || 'None'}\\n`;\n    report += `- Reference: ${externalRef}\\n`;\n}\n\nif (dataList.length === 0 || raw.query_status !== \"ok\") {\n    return {\n        \"threatfox\": {\n            \"no_data\": true,\n            \"threatfox_report\": \"SOURCE: Abuse_ThreatFox\\nSTATUS: No indicators found.\",\n            \"mongo_id\": mongoId\n        }\n    };\n}\n\nreturn {\n    \"threatfox\": {\n        \"no_data\": false,\n        \"threatfox_report\": report,\n        \"threatfox_active\": dataList.length > 0,\n        \"threatfox_max_confidence\": Math.max(...confidenceScores, 0),\n        \"mongo_id\": mongoId\n    }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "22f949e1-9fc7-41b6-bcec-3582c355a217",
      "name": "Data reduction and aggregation - VirusTotal",
      "type": "n8n-nodes-base.code",
      "position": [
        -1376,
        -352
      ],
      "parameters": {
        "jsCode": "// Get item from n8n input\nconst item = $input.item.json;\n\nconst mongoId = item.id || null;\n\n// Target the correct path in your specific JSON\nconst attr = item.raw_data && item.raw_data.attributes ? item.raw_data.attributes : {};\n\n// 1. Get Analysis Stats\nconst stats = attr.last_analysis_stats || {};\nconst malCount = stats.malicious || 0;\nconst susCount = stats.suspicious || 0;\nconst undCount = stats.undetected || 0;\n\n// 2. Get Engines that flagged it (Malicious/Suspicious)\nconst results = attr.last_analysis_results || {};\nconst flaggedBy = [];\n\nObject.keys(results).forEach(engine => {\n    if (results[engine].category === 'malicious' || results[engine].category === 'suspicious') {\n        flaggedBy.push(`${engine} (${results[engine].result})`);\n    }\n});\n\n// 3. Network & Ownership\nconst asOwner = attr.as_owner || \"Unknown Owner\";\nconst network = attr.network || \"Unknown Network\";\nconst rdap = attr.rdap || {};\nconst ipRange = (rdap.start_address && rdap.end_address) \n    ? `${rdap.start_address} - ${rdap.end_address}` \n    : \"Range not specified\";\n\n// 4. Build the report string\nlet report = `SOURCE: VirusTotal\\n`;\nreport += `- Owner: ${asOwner}\\n`;\nreport += `- Network: ${network}\\n`;\nreport += `- IP Range: ${ipRange}\\n`;\nreport += `- Stats: Malicious: ${malCount}, Suspicious: ${susCount}, Undetected: ${undCount}\\n`;\n\nif (flaggedBy.length > 0) {\n    report += `- Flagged by: ${flaggedBy.join(', ')}\\n`;\n}\n\n// 5. Check if it's a Big Player\nconst bigPlayers = [\"github\", \"google\", \"microsoft\", \"amazon\", \"cloudflare\"];\nconst isBigPlayer = bigPlayers.some(p => asOwner.toLowerCase().includes(p));\n\nif (!item.raw_data || !item.raw_data.attributes) {\n    return {\n        \"virustotal\": {\n            \"no_data\": true,\n            \"vt_report\": \"SOURCE: VirusTotal\\nSTATUS: No data found for this resource.\",\n            \"mongo_id\": mongoId\n        }\n    };\n}\n\nreturn {\n    \"virustotal\": {\n        \"no_data\": false,\n        \"vt_report\": report,\n        \"vt_stats\": stats,\n        \"vt_owner\": asOwner,\n        \"vt_is_big_player\": isBigPlayer,\n        \"vt_malicious_count\": malCount,\n        \"vt_scan_date\": item.scan_date,\n        \"mongo_id\": mongoId\n    }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "5116e479-f768-40d5-bda2-5a11ed457aff",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1104,
        -624
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3.2
    },
    {
      "id": "b14246cd-3917-44a5-9ebc-27e90118387c",
      "name": "Code for Merge",
      "type": "n8n-nodes-base.code",
      "position": [
        -928,
        -784
      ],
      "parameters": {
        "jsCode": "// \u0141\u0105czymy wszystkie elementy z wej\u015bcia w jeden obiekt\nlet combinedData = {};\n\nfor (const item of $input.all()) {\n  // Scalamy w\u0142a\u015bciwo\u015bci (np. virustotal, threatfox, urlhaus) w jeden obiekt\n  Object.assign(combinedData, item.json);\n}\n\nreturn combinedData;"
      },
      "typeVersion": 2
    },
    {
      "id": "34ac24c9-e2bf-4f03-ad2a-8c8c57218632",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3136,
        -592
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 288,
        "content": "## Orchestration & Data Ingestion\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c79b8cfa-45c8-4173-a58d-62a6adc8679d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2400,
        -992
      ],
      "parameters": {
        "color": 7,
        "width": 576,
        "height": 992,
        "content": "## Multi-Source Threat Intelligence Enrichment\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4a9b306d-a43b-49b3-8c60-1f29ccb12798",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1808,
        -1088
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 1232,
        "content": "## Dual-Layer Persistence Strategy\n"
      },
      "typeVersion": 1
    },
    {
      "id": "733bed6a-a760-4f3e-9cfa-507a133d3e6e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1152,
        -864
      ],
      "parameters": {
        "color": 7,
        "width": 800,
        "height": 640,
        "content": "## AI Synthesis & Cognitive Analysis\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9397274e-2b43-4311-bb10-71917f04eaa2",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -800
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 528,
        "content": "## Incident Reporting & Remediation\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "283c9478-9c26-457b-8523-e2fe91da72cc",
      "name": "If query_status = ok - UrlHause",
      "type": "n8n-nodes-base.if",
      "position": [
        -1968,
        -912
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f2e261a0-b1c9-4db5-8d57-112a5d5e20f7",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query_status }}",
              "rightValue": "ok"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "12f99d4d-1cee-47ab-bef6-3770dd24fea0",
      "name": "If query_status = ok - ThretFox",
      "type": "n8n-nodes-base.if",
      "position": [
        -1968,
        -528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "024c712f-29c5-49ed-8152-ac5fafac7816",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.query_status }}",
              "rightValue": "ok"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "3ada188a-28ca-46c6-8387-a07bf03fdd63",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3840,
        -784
      ],
      "parameters": {
        "width": 688,
        "height": 608,
        "content": "# \ud83d\udee1\ufe0f Project: Cyber Sentinel\n\n### **Purpose**\nAutonomous **Cyber Threat Intelligence (CTI)** & **Passive DNS Monitoring**. This workflow orchestrates the analysis of DNS telemetry using AI to detect malicious patterns and trigger autonomous response playbooks.\n\n---\n\n### **\ud83d\ude80 Setup Instructions**\n1.  **Secret Management:** Ensure **HashiCorp Vault** is connected to handle API keys and credentials securely.\n2.  **AI Engine:** Configure the **Ollama/Gemini** node to enable bilingual threat assessment (**EN/PL**).\n3.  **Data Layer:** Verify the connection to the **PostgreSQL** database (Neural Lake) for indicator enrichment.\n\n---\n\n### **\ud83d\udd17 Resources**\n* **Documentation:** [https://lukaszfd.github.io/cyber-sentinel/](https://lukaszfd.github.io/cyber-sentinel/)\n* **Author:** \u0141ukasz Dejko"
      },
      "typeVersion": 1
    },
    {
      "id": "dddbc5cf-3aa9-4e61-88ea-abecf53454ec",
      "name": "Select threat scale for AI agent",
      "type": "n8n-nodes-base.mySql",
      "position": [
        -544,
        -784
      ],
      "parameters": {
        "query": "SELECT GROUP_CONCAT(formatted_line SEPARATOR '\\n') AS threat_scale\nFROM v_threat_scale_for_agent;",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "mySql": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "timeSavedMode": "fixed",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "8c0792c3-8584-4396-b6b6-394e646cab08",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Code for Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Parse AI Agent output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "MySQL Pass": {
      "main": [
        [
          {
            "node": "Mongo DB Pass",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mongo DB Pass": {
      "main": [
        [
          {
            "node": "Select rows from a table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send an Email": {
      "main": [
        []
      ]
    },
    "Code for Merge": {
      "main": [
        [
          {
            "node": "Gemini AI Studio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Email Pass": {
      "main": [
        [
          {
            "node": "Send an Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Abuse API TOKEN": {
      "main": [
        [
          {
            "node": "Abuse.CH_ThreatFox request",
            "type": "main",
            "index": 0
          },
          {
            "node": "Abuse.CH_URLHaus",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Abuse.CH_URLHaus": {
      "main": [
        [
          {
            "node": "If query_status = ok - UrlHause",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini AI Studio": {
      "main": [
        [
          {
            "node": "Select threat scale for AI agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "MySQL Pass",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert AI verdict": {
      "main": [
        [
          {
            "node": "Get Email Pass",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert doc URLHaus": {
      "main": [
        [
          {
            "node": "Data reduction and aggregation - Urlhaus",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "VirusTotal IP Scan": {
      "main": [
        [
          {
            "node": "If 'malicious' or 'suspicious'",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert doc ThreatFox": {
      "main": [
        [
          {
            "node": "Data reduction and aggregation - ThreatFox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert doc VirusTotal": {
      "main": [
        [
          {
            "node": "Data reduction and aggregation - VirusTotal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Agent output": {
      "main": [
        [
          {
            "node": "Insert AI verdict",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Virus total API TOKEN": {
      "main": [
        [
          {
            "node": "VirusTotal IP Scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Select rows from a table": {
      "main": [
        [
          {
            "node": "Virus total API TOKEN",
            "type": "main",
            "index": 0
          },
          {
            "node": "Abuse API TOKEN",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Abuse.CH_ThreatFox request": {
      "main": [
        [
          {
            "node": "If query_status = ok - ThretFox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Json for Mongo - URLHaus": {
      "main": [
        [
          {
            "node": "Insert doc URLHaus",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If 'malicious' or 'suspicious'": {
      "main": [
        [
          {
            "node": "Edit Json for Mongo - VirusTotal",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Insert a clear scan - VirusTotal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Json for Mongo - ThreatFox": {
      "main": [
        [
          {
            "node": "Insert doc ThreatFox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If query_status = ok - ThretFox": {
      "main": [
        [
          {
            "node": "Edit Json for Mongo - ThreatFox",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Insert a clear scan - ThreatFox",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If query_status = ok - UrlHause": {
      "main": [
        [
          {
            "node": "Edit Json for Mongo - URLHaus",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Insert a clear scan - URLHaus",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Json for Mongo - VirusTotal": {
      "main": [
        [
          {
            "node": "Insert doc VirusTotal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select threat scale for AI agent": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data reduction and aggregation - Urlhaus": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data reduction and aggregation - ThreatFox": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Data reduction and aggregation - VirusTotal": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    }
  }
}