{
  "name": "Send a weekly patch-priority digest from the CISA Known Exploited Vulnerabilities catalog",
  "nodes": [
    {
      "parameters": {
        "content": "### How it works\n**For:** IT, patching and vulnerability-management teams that want a short weekly list of actively exploited vulnerabilities affecting the vendors they run.\n\n1. Runs every Monday at 08:00 (or manually).\n2. Downloads the public CISA Known Exploited Vulnerabilities (KEV) JSON feed (no API key).\n3. Keeps entries added in the previous week (Mon-Sun, by CISA's `dateAdded` date, compared with the run's UTC date) and marks those whose vendor is on your watchlist. Entries added on the run day go into next week's digest.\n4. Emails an HTML digest with CVE, product, due date and required action. Watchlist hits are listed first.\n\n### Setup\n**Requires:** n8n (tested on self-hosted 2.44.7; n8n Cloud not tested), an SMTP account. Internet access to cisa.gov.\n\n1. Edit `WATCHLIST` in **Filter & build digest** (keep `LOOKBACK_DAYS` at 7 for the weekly schedule).\n2. Attach an SMTP credential to **Email digest** and replace `REPLACE-patch-team@example.com`.\n3. Click **Test workflow**: it downloads the live feed and stops before sending. To send one real digest, run it from the **Every Monday 08:00** trigger instead.\n4. Activate the workflow.\n\n**Time zone:** 08:00 is the workflow's time zone (Settings > Timezone). More than 8 hours ahead of UTC, digests cover Sun-Sat. Each entry is reported once if it runs every Monday; a missed run loses that week (re-run it that day from the Every Monday 08:00 node).\n\n### Customization\nSwap the email node for Slack/Teams, or add EPSS scores with a second HTTP Request.\n\nData: CISA Known Exploited Vulnerabilities Catalog (https://www.cisa.gov/known-exploited-vulnerabilities-catalog), CC0 1.0. Use of this data does not imply endorsement by CISA or DHS.\n\n_Built with AI assistance and tested on n8n 2.44.7. Not affiliated with n8n or CISA. n8n is a trademark of n8n GmbH._",
        "height": 780,
        "width": 460
      },
      "id": "4edbfa44-d00c-45d3-8ad4-250d2c39bbef",
      "name": "Sticky Note 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1180,
        -460
      ]
    },
    {
      "parameters": {},
      "id": "c3fed16e-5764-4b7a-9ba9-b7443dd5f0c5",
      "name": "Test workflow (manual)",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -600,
        220
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "1b84b536-954d-404a-b58c-2ad3fc027993",
      "name": "Every Monday 08:00",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -600,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{json:{_dryRun:true}}]; // manual tests never send"
      },
      "id": "9f6ad1a2-eafe-4282-9913-46a239e88a53",
      "name": "Flag: dry run",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -380,
        220
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{json:{_dryRun:false}}];"
      },
      "id": "4aaf2987-5efc-4e1f-8c89-6df1ff19c9a0",
      "name": "Flag: live run",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -380,
        0
      ]
    },
    {
      "parameters": {
        "url": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "options": {
          "timeout": 30000
        }
      },
      "id": "2d40825e-b9bf-403b-9513-c6884ce45992",
      "name": "Download CISA KEV feed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -140,
        100
      ]
    },
    {
      "parameters": {
        "jsCode": "const WATCHLIST = ['microsoft','google','apple','fortinet','citrix','ivanti','atlassian','oracle','adobe','apache'];\nconst LOOKBACK_DAYS = 7; // keep 7 for the weekly Monday schedule\nlet dry = false; try { dry = $('Flag: dry run').isExecuted === true; } catch (e) {}\nconst feed = $input.first().json;\nconst vulns = Array.isArray(feed.vulnerabilities) ? feed.vulnerabilities : [];\nif (!vulns.length) throw new Error('KEV feed returned no vulnerabilities; check the URL or network.');\n// dateAdded is date-only (YYYY-MM-DD), so compare UTC date strings, not timestamps.\nconst day = d => d.toISOString().slice(0,10);\nconst end = day(new Date());                                   // exclusive: today's additions go into next week's digest\nconst start = day(new Date(Date.now() - LOOKBACK_DAYS*864e5)); // inclusive\nconst last = day(new Date(Date.parse(end + 'T00:00:00Z') - 864e5));\nconst period = (LOOKBACK_DAYS === 7 && new Date(start + 'T00:00:00Z').getUTCDay() === 1) ? `in the previous week (Mon-Sun, ${start} to ${last})` : `from ${start} to ${last}`;\nconst recent = vulns.filter(v => typeof v.dateAdded === 'string' && v.dateAdded >= start && v.dateAdded < end)\n  .map(v => ({...v, watch: WATCHLIST.includes(String(v.vendorProject).toLowerCase())}))\n  .sort((a,b) => (b.watch - a.watch) || (a.dueDate > b.dueDate ? 1 : -1));\nconst esc = s => String(s ?? '').replace(/[<>&]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;'}[c]));\nconst rows = recent.map(v => `<tr><td>${v.watch?'&#9733;':''}</td><td>${esc(v.cveID)}</td><td>${esc(v.vendorProject)} ${esc(v.product)}</td><td>${esc(v.dateAdded)}</td><td>${esc(v.dueDate)}</td><td>${esc(v.knownRansomwareCampaignUse)}</td><td>${esc(v.requiredAction)}</td></tr>`).join('');\nreturn [{json:{_dryRun:dry, catalogVersion:feed.catalogVersion, totalInCatalog:vulns.length, windowStart:start, windowEnd:last, newCount:recent.length,\n  watchHits:recent.filter(v=>v.watch).length, cves:recent.map(v=>v.cveID),\n  html: recent.length ? `<p>${recent.length} KEV entries added ${period}; ${recent.filter(v=>v.watch).length} on your watchlist (marked &#9733;). Catalog ${esc(feed.catalogVersion)}.</p><table border=\"1\" cellpadding=\"4\"><tr><th></th><th>CVE</th><th>Product</th><th>Added</th><th>CISA due date (US federal agencies)</th><th>Ransomware use</th><th>Required action</th></tr>${rows}</table><p>Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA.</p>` : `<p>No new KEV entries added ${period}.</p><p>Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA.</p>`}}];"
      },
      "id": "e5d54d7a-fcdd-4150-9bd3-b6862748b15d",
      "name": "Filter & build digest",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        80,
        100
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "bfffeddd-a837-4ed4-806d-c18ee3cebab6",
              "leftValue": "={{ $json._dryRun === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "d25ec26d-89f3-4667-a717-a2b803fdbf50",
      "name": "Dry run?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        300,
        100
      ]
    },
    {
      "parameters": {},
      "id": "043644ac-b864-4bd8-bb4a-a8a542dfe669",
      "name": "Dry run: log only",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        520,
        -20
      ]
    },
    {
      "parameters": {
        "fromEmail": "it-automation@example.com",
        "toEmail": "REPLACE-patch-team@example.com",
        "subject": "=Weekly KEV patch digest: {{ $json.newCount }} new ({{ $json.watchHits }} on watchlist)",
        "emailFormat": "html",
        "html": "={{ $json.html }}",
        "options": {}
      },
      "id": "79fa6695-5891-4ebd-be2b-9d60d0a4da9c",
      "name": "Email digest",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        520,
        180
      ]
    },
    {
      "parameters": {
        "content": "## 1 \u00b7 Schedule & download\nWeekly run or manual dry run; fetches the public CISA KEV JSON.",
        "height": 510,
        "width": 640,
        "color": 7
      },
      "id": "135572cd-c349-4080-892d-855c23567276",
      "name": "Sticky Note 2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -640,
        -150
      ]
    },
    {
      "parameters": {
        "content": "## 2 \u00b7 Filter & send\nKeeps the previous week (Mon-Sun), stars watchlist vendors, emails the digest.",
        "height": 490,
        "width": 620,
        "color": 7
      },
      "id": "e328d2ae-1122-42bb-822e-df5d62569fee",
      "name": "Sticky Note 3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        40,
        -170
      ]
    }
  ],
  "connections": {
    "Every Monday 08:00": {
      "main": [
        [
          {
            "node": "Flag: live run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test workflow (manual)": {
      "main": [
        [
          {
            "node": "Flag: dry run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag: live run": {
      "main": [
        [
          {
            "node": "Download CISA KEV feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Flag: dry run": {
      "main": [
        [
          {
            "node": "Download CISA KEV feed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download CISA KEV feed": {
      "main": [
        [
          {
            "node": "Filter & build digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter & build digest": {
      "main": [
        [
          {
            "node": "Dry run?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dry run?": {
      "main": [
        [
          {
            "node": "Dry run: log only",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}