{
  "name": "Palo Alto PSIRT Firewall Monitor",
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [1],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "df8af874-cc06-4988-9c67-067149ac0586",
      "name": "Weekly PSIRT Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [-640, 400]
    },
    {
      "parameters": {
        "url": "https://security.paloaltonetworks.com/rss.xml",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          },
          "timeout": 20000
        }
      },
      "id": "9b64acfa-5e46-4cb4-b455-feb3a4b4a899",
      "name": "Fetch Palo PSIRT Feed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [-416, 400]
    },
    {
      "parameters": {
        "jsCode": "const xml = ($input.first().json.data || '').toString();\nconst cutoff = Date.now() - 30*24*60*60*1000;\nconst strip = (s) => s.replace(/<!\\[CDATA\\[|\\]\\]>/g,'').replace(/<[^>]+>/g,'').trim();\nconst first = (block, re) => { const m = block.match(re); return m ? strip(m[1]) : ''; };\nconst advisories = [];\nconst items = xml.match(/<item[\\s\\S]*?<\\/item>/gi) || [];\nfor (const block of items) {\n  const pubDate = first(block, /<pubDate[^>]*>([\\s\\S]*?)<\\/pubDate>/i);\n  const ts = pubDate ? Date.parse(pubDate) : NaN;\n  if (!isNaN(ts) && ts < cutoff) continue;\n  advisories.push({\n    title: first(block, /<title[^>]*>([\\s\\S]*?)<\\/title>/i),\n    link: first(block, /<link[^>]*>([\\s\\S]*?)<\\/link>/i),\n    pubDate,\n    description: first(block, /<description[^>]*>([\\s\\S]*?)<\\/description>/i)\n  });\n}\nif (advisories.length === 0) return [];\nreturn [{ json: { count: advisories.length, checkedAt: new Date().toISOString(), advisories } }];"
      },
      "id": "216f7b79-322d-46f5-8383-14652bf6db0a",
      "name": "Parse Recent Advisories",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [-192, 400]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Analyze these Palo Alto Networks PSIRT advisories published in the last 30 days and determine whether OUR firewall requires action.\n\nAdvisories (JSON):\n{{ JSON.stringify($json.advisories, null, 2) }}",
        "hasOutputParser": true,
        "options": {
          "systemMessage": "You are a Palo Alto Networks security operations analyst.\nYou have access to MCP tools (the 'Palo Firewall MCP' tool) that query a LIVE PAN-OS firewall. Use them to retrieve the firewall's model and running PAN-OS software version (e.g. the system-info / version / show-system-info style tools). Always call the tools to get real data — never assume the version.\n\nFor each PSIRT advisory in the user message:\n1. Identify the affected product(s), the affected PAN-OS version range(s), and the CVE(s).\n2. Compare against the firewall's ACTUAL model and running PAN-OS version obtained from the MCP tools.\n3. Decide whether this firewall is affected and therefore action is needed.\n\nRules:\n- Set actionNeeded=true only if at least one advisory affects THIS firewall's product AND its running version.\n- If no advisory affects this firewall, set actionNeeded=false and affectedAdvisories=[].\n- If you cannot reach the firewall or determine its version, set actionNeeded=true, riskLevel=\"unknown\", and explain in summary that the version could not be verified.\n\nOutput: respond strictly in the required JSON schema. In emailHtml, write a concise, well-formatted HTML alert containing a heading, the firewall model + running PAN-OS version, and a list/table of affected advisories (CVE, severity, affected vs fixed version, recommended action, and any interim mitigation), plus a link to each advisory. emailSubject must be a single concise line.",
          "maxIterations": 15
        }
      },
      "id": "95f8058b-64db-4625-ac3c-ada28694db64",
      "name": "Assess Firewall Exposure",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [80, 400]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-6",
          "cachedResultName": "Claude Sonnet 4.6"
        },
        "options": {
          "maxTokensToSample": 20000,
          "temperature": 0
        }
      },
      "id": "576b15c3-a918-4922-b54c-24f904207741",
      "name": "Claude Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.5,
      "position": [-80, 640]
    },
    {
      "parameters": {
        "endpointUrl": "https://YOUR-MCP-GATEWAY.example.com/mcp",
        "options": {
          "timeout": 60000
        }
      },
      "id": "c0bf1e58-5509-44d3-b9a7-5ee68f4fc1d3",
      "name": "Palo Firewall MCP",
      "type": "@n8n/n8n-nodes-langchain.mcpClientTool",
      "typeVersion": 1.3,
      "position": [64, 640]
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\"actionNeeded\": true, \"firewallModel\": \"PA-3220\", \"panosVersion\": \"11.1.3\", \"riskLevel\": \"high\", \"summary\": \"One critical advisory (CVE-2024-3400) affects this firewall running PAN-OS 11.1.3.\", \"affectedAdvisories\": [{\"advisoryId\": \"PAN-SA-2024-0001\", \"cve\": \"CVE-2024-3400\", \"title\": \"PAN-OS: OS Command Injection in GlobalProtect\", \"severity\": \"Critical\", \"affectedVersions\": \"< 11.1.2-h3\", \"fixedVersion\": \"11.1.2-h3 or later\", \"recommendedAction\": \"Upgrade PAN-OS to 11.1.2-h3; apply Threat Prevention signatures as interim mitigation.\", \"link\": \"https://security.paloaltonetworks.com/CVE-2024-3400\"}], \"emailSubject\": \"[PSIRT] Action needed: PA-3220 exposed to CVE-2024-3400\", \"emailHtml\": \"<h2>Palo Alto PSIRT Alert</h2><p>PA-3220 running PAN-OS 11.1.3 is affected by 1 advisory.</p>\"}"
      },
      "id": "fc59f11a-6e4a-43df-9427-c883ddf9e711",
      "name": "Exposure Report",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [224, 640]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ $json.output.actionNeeded }}",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "rightValue": ""
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "6c3b515a-c29d-4b04-aacd-c4bcb9c21b7c",
      "name": "Action Needed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [368, 400]
    },
    {
      "parameters": {
        "sendTo": "you@example.com",
        "subject": "={{ $('Assess Firewall Exposure').item.json.output.emailSubject }}",
        "message": "={{ $('Assess Firewall Exposure').item.json.output.emailHtml }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "04082124-08cd-4179-8848-fa9ed2bc7584",
      "name": "Send Alert Email",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [608, 320]
    },
    {
      "parameters": {
        "content": "## PSIRT source\nFetches the Palo Alto Networks Security Advisories RSS feed and keeps only items from the last 30 days (Palo Alto typically publishes advisories in monthly batches, so a 7-day window can come up empty).\n\n**Verify the feed URL** (https://security.paloaltonetworks.com/rss.xml) is reachable from your n8n instance; swap it if Palo changes the endpoint.",
        "height": 232,
        "width": 600,
        "color": 4
      },
      "id": "84400865-bdba-4b9e-8c0b-264215797b2f",
      "name": "Sticky Note 822159cd",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-720, 112]
    },
    {
      "parameters": {
        "content": "## Configure the Palo MCP tool\n1. Set Palo Firewall MCP -> Endpoint URL to your Palo Alto MCP server (SSE or Streamable HTTP).\n2. If your MCP server requires auth, change Authentication from none to Bearer/Header and attach a credential.\n3. The agent uses the MCP tools to read the firewall model + running PAN-OS version and compares it to each advisory.\n\n**Model tip:** use an Anthropic Claude model here. MCP gateways can expose dozens of tools, and some providers reject complex tool JSON schemas (Gemini returns a 400 on union-typed properties). Claude handles them natively.",
        "height": 440,
        "width": 620,
        "color": 5
      },
      "id": "a204d451-2ec5-4a7a-9b43-bbbe5ce39327",
      "name": "Sticky Note 4f0544f6",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [-112, -64]
    }
  ],
  "connections": {
    "Weekly PSIRT Check": {
      "main": [
        [
          {
            "node": "Fetch Palo PSIRT Feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Palo PSIRT Feed": {
      "main": [
        [
          {
            "node": "Parse Recent Advisories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Recent Advisories": {
      "main": [
        [
          {
            "node": "Assess Firewall Exposure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assess Firewall Exposure": {
      "main": [
        [
          {
            "node": "Action Needed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Model": {
      "ai_languageModel": [
        [
          {
            "node": "Assess Firewall Exposure",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Palo Firewall MCP": {
      "ai_tool": [
        [
          {
            "node": "Assess Firewall Exposure",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Exposure Report": {
      "ai_outputParser": [
        [
          {
            "node": "Assess Firewall Exposure",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Action Needed?": {
      "main": [
        [
          {
            "node": "Send Alert Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {}
}
