AutomationFlowsAI & RAG › Jovanops Security Pulse

Jovanops Security Pulse

JovanOps Security Pulse. Uses httpRequest, chainLlm, lmChatGoogleGemini, gmail. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexityAI-powered10 nodesHTTP RequestChain LlmGoogle Gemini ChatGmail
AI & RAG Trigger: Event Nodes: 10 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Chainllm → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "JovanOps Security Pulse",
  "nodes": [
    {
      "parameters": {},
      "id": "4b905668-e237-4d67-aea8-5990e148c803",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        112
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "49b30c3e-acfa-4d9f-8a45-432c9415cb1a",
              "name": "target_url",
              "value": "https://jovanops.com/",
              "type": "string"
            },
            {
              "id": "cf15d4bf-3118-443a-92b8-64844053fad5",
              "name": "owner",
              "value": "Jovan Ljusi\u0107",
              "type": "string"
            },
            {
              "id": "24afc7ad-58e2-47e6-80ec-f32fca866f01",
              "name": "high_risk_threshold",
              "value": 40,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "2bdef6b6-ba5c-45f9-83cb-37acf8dd3d2d",
      "name": "Target Configuration",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        224,
        112
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.target_url }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "text",
              "outputPropertyName": "body"
            }
          },
          "timeout": 10000
        }
      },
      "id": "51136f2f-368f-49dc-9001-9c090a79a7d1",
      "name": "Fetch Website Response",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        448,
        112
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst config = $(\"Target Configuration\").first().json;\n\nconst targetUrl = config.target_url;\nconst highRiskThreshold = Number(config.high_risk_threshold ?? 40);\nconst statusCode = Number(response.statusCode ?? response.status ?? 0);\n\nconst originalHeaders = response.headers ?? {};\nconst headers = {};\n\nfor (const [key, value] of Object.entries(originalHeaders)) {\n  headers[key.toLowerCase()] = String(value);\n}\n\nconst contentSecurityPolicy =\n  headers[\"content-security-policy\"] ?? \"\";\n\nconst checks = [\n  {\n    control: \"Website availability\",\n    passed: statusCode >= 200 && statusCode < 400,\n    weight: 20,\n    recommendation:\n      \"Investigate the website hosting, DNS, deployment, or application availability.\",\n  },\n  {\n    control: \"HTTPS\",\n    passed: targetUrl.startsWith(\"https://\"),\n    weight: 15,\n    recommendation:\n      \"Redirect all unencrypted HTTP traffic to HTTPS.\",\n  },\n  {\n    control: \"Strict-Transport-Security\",\n    passed: Boolean(headers[\"strict-transport-security\"]),\n    weight: 15,\n    recommendation:\n      \"Configure the Strict-Transport-Security header after confirming HTTPS is fully enabled.\",\n  },\n  {\n    control: \"Content-Security-Policy\",\n    passed: Boolean(contentSecurityPolicy),\n    weight: 15,\n    recommendation:\n      \"Implement a restrictive Content-Security-Policy appropriate for the website.\",\n  },\n  {\n    control: \"Clickjacking protection\",\n    passed:\n      Boolean(headers[\"x-frame-options\"]) ||\n      contentSecurityPolicy.toLowerCase().includes(\"frame-ancestors\"),\n    weight: 10,\n    recommendation:\n      \"Configure X-Frame-Options or the CSP frame-ancestors directive.\",\n  },\n  {\n    control: \"X-Content-Type-Options\",\n    passed:\n      (headers[\"x-content-type-options\"] ?? \"\").toLowerCase() ===\n      \"nosniff\",\n    weight: 10,\n    recommendation:\n      \"Set X-Content-Type-Options to nosniff.\",\n  },\n  {\n    control: \"Referrer-Policy\",\n    passed: Boolean(headers[\"referrer-policy\"]),\n    weight: 5,\n    recommendation:\n      \"Configure an appropriate Referrer-Policy.\",\n  },\n  {\n    control: \"Permissions-Policy\",\n    passed: Boolean(headers[\"permissions-policy\"]),\n    weight: 5,\n    recommendation:\n      \"Restrict unnecessary browser features using Permissions-Policy.\",\n  },\n  {\n    control: \"Technology disclosure\",\n    passed: !headers[\"x-powered-by\"],\n    weight: 5,\n    recommendation:\n      \"Remove the X-Powered-By header to reduce unnecessary technology disclosure.\",\n  },\n];\n\nconst securityScore = checks.reduce(\n  (total, check) => total + (check.passed ? check.weight : 0),\n  0\n);\n\nconst riskScore = 100 - securityScore;\n\nlet severity = \"LOW\";\n\nif (riskScore >= 60) {\n  severity = \"CRITICAL\";\n} else if (riskScore >= highRiskThreshold) {\n  severity = \"HIGH\";\n} else if (riskScore >= 20) {\n  severity = \"MEDIUM\";\n}\n\nconst findings = checks\n  .filter((check) => !check.passed)\n  .map((check) => ({\n    control: check.control,\n    risk_points: check.weight,\n    recommendation: check.recommendation,\n  }));\n\nreturn [\n  {\n    json: {\n      target_url: targetUrl,\n      owner: config.owner,\n      scanned_at: new Date().toISOString(),\n      status_code: statusCode,\n      security_score: securityScore,\n      risk_score: riskScore,\n      severity,\n      high_risk_threshold: highRiskThreshold,\n      passed_controls: checks.filter((check) => check.passed).length,\n      failed_controls: findings.length,\n      findings,\n    },\n  },\n];"
      },
      "id": "65a2eec9-0e60-40f1-a456-ecfe9779d6e9",
      "name": "Analyze Security Controls",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        112
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You are a defensive cybersecurity reporting assistant.\n\nAnalyze the following results from a passive website configuration review.\n\nImportant rules:\n- This is not a penetration test.\n- Do not invent vulnerabilities.\n- Do not recommend exploitation, brute force, bypassing controls, or intrusive testing.\n- Base the report only on the supplied findings.\n- Treat the risk score as an internal workflow score, not as an industry-standard severity rating.\n- Provide clear and practical defensive recommendations.\n- Do not claim that a confirmed vulnerability exists solely because a security header is missing. Describe missing headers as configuration or security-hardening gaps.\nWebsite: {{ $('Analyze Security Controls').first().json.target_url }}\nHTTP status: {{ $('Analyze Security Controls').first().json.status_code }}\nSecurity score: {{ $('Analyze Security Controls').first().json.security_score }}/100\nRisk score: {{ $('Analyze Security Controls').first().json.risk_score }}/100\nWorkflow severity: {{ $('Analyze Security Controls').first().json.severity }}\nPassed controls: {{ $('Analyze Security Controls').first().json.passed_controls }}\nFailed controls: {{ $('Analyze Security Controls').first().json.failed_controls }}\n\nFailed security controls:\n{{ JSON.stringify($('Analyze Security Controls').first().json.findings) }}\n\nCreate a concise report with these sections:\n\n1. Executive Summary\n2. Detected Configuration Gaps\n3. Prioritized Remediation Actions\n4. Assessment Limitations\n- Use plain text only.\n- Do not use Markdown symbols such as #, *, backticks, or tables.",
        "batching": {}
      },
      "id": "ce26661c-c4ab-462f-95f9-10c1c40264a3",
      "name": "Generate Remediation Report",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        896,
        112
      ]
    },
    {
      "parameters": {
        "modelName": "models/gemini-3.1-flash-lite",
        "options": {}
      },
      "id": "a9d7dbd9-4fab-40cf-a0fe-fbc033890376",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1.1,
      "position": [
        976,
        336
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "ca0142ae-6124-4999-831f-af0e0b563f56",
              "leftValue": "={{ Number($('Analyze Security Controls').first().json.risk_score) >= Number($('Analyze Security Controls').first().json.high_risk_threshold) }}",
              "rightValue": "={{ $('Analyze Security Controls').first().json.high_risk_threshold }}",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1248,
        112
      ],
      "id": "efe50254-e521-428b-be4a-8ade1471daae",
      "name": "Is Risk High?"
    },
    {
      "parameters": {
        "sendTo": "your-email@example.com",
        "subject": "={{ '[JovanOps Security Alert] ' + $('Analyze Security Controls').first().json.severity + ' risk detected' }}",
        "emailType": "text",
        "message": "=JovanOps Security Pulse detected a high-risk configuration result.  Website: {{ $('Analyze Security Controls').first().json.target_url }} Scan time: {{ $('Analyze Security Controls').first().json.scanned_at }} HTTP status: {{ $('Analyze Security Controls').first().json.status_code }} Security score: {{ $('Analyze Security Controls').first().json.security_score }}/100 Risk score: {{ $('Analyze Security Controls').first().json.risk_score }}/100 Workflow severity: {{ $('Analyze Security Controls').first().json.severity }}  AI-generated remediation report:  {{ $('Generate Remediation Report').first().json.text }}  Important: This was a passive security-configuration review, not a penetration test.",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        1456,
        16
      ],
      "id": "8fbb2313-42b8-4401-86b6-1565afa8b5d4",
      "name": "Send Urgent Security Alert"
    },
    {
      "parameters": {
        "sendTo": "your-email@example.com",
        "subject": "={{ '[JovanOps Security Report] Scan completed - Security score ' + $('Analyze Security Controls').first().json.security_score + '/100' }}",
        "emailType": "text",
        "message": "=JovanOps Security Pulse completed a routine website security review.  Website: {{ $('Analyze Security Controls').first().json.target_url }} Scan time: {{ $('Analyze Security Controls').first().json.scanned_at }} HTTP status: {{ $('Analyze Security Controls').first().json.status_code }} Security score: {{ $('Analyze Security Controls').first().json.security_score }}/100 Risk score: {{ $('Analyze Security Controls').first().json.risk_score }}/100 Workflow severity: {{ $('Analyze Security Controls').first().json.severity }}  AI-generated remediation report:  {{ $('Generate Remediation Report').first().json.text }}  Important: This was a passive security-configuration review, not a penetration test.",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        1456,
        208
      ],
      "id": "a67d9ab6-46b8-4a96-a8b9-23b42d0b7dce",
      "name": "Send Routine Security Report"
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        192,
        272
      ],
      "id": "77e89c6a-11ce-49a9-8495-3493fb7b73b5",
      "name": "Daily Security Scan"
    }
  ],
  "connections": {
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Target Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Target Configuration": {
      "main": [
        [
          {
            "node": "Fetch Website Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Website Response": {
      "main": [
        [
          {
            "node": "Analyze Security Controls",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Security Controls": {
      "main": [
        [
          {
            "node": "Generate Remediation Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Remediation Report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate Remediation Report": {
      "main": [
        [
          {
            "node": "Is Risk High?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Risk High?": {
      "main": [
        [
          {
            "node": "Send Urgent Security Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Routine Security Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Security Scan": {
      "main": [
        [
          {
            "node": "Target Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "nodeGroups": [],
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

JovanOps Security Pulse. Uses httpRequest, chainLlm, lmChatGoogleGemini, gmail. Event-driven trigger; 10 nodes.

Source: https://github.com/JovanOps/jovanops-security-pulse/blob/main/workflow/jovanops-security-pulse-public.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

🤖🧑‍💻 AI Agent for Top n8n Creators Leaderboard Reporting. Uses httpRequest, lmChatOpenAi, executeWorkflowTrigger, toolWorkflow. Event-driven trigger; 49 nodes.

HTTP Request, OpenAI Chat, Execute Workflow Trigger +8
AI & RAG

🤖🧑‍💻 AI Agent for Top n8n Creators Leaderboard Reporting. Uses httpRequest, lmChatOpenAi, executeWorkflowTrigger, toolWorkflow. Event-driven trigger; 49 nodes.

HTTP Request, OpenAI Chat, Execute Workflow Trigger +8
AI & RAG

This n8n workflow is designed to automate the aggregation, processing, and reporting of community statistics related to n8n creators and workflows. Its primary purpose is to generate insightful report

HTTP Request, OpenAI Chat, Execute Workflow Trigger +8
AI & RAG

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

HTTP Request, Google Sheets, OpenRouter Chat +5
AI & RAG

Powertech Whatsapp. Uses whatsAppTrigger, whatsApp, httpRequest, googleGemini. Event-driven trigger; 36 nodes.

WhatsApp Trigger, WhatsApp, HTTP Request +8