{
  "name": "ASM \u2014 Daily scan + Slack summary + per-finding alert + GRC email",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "id": "b927ffe3-f969-42e6-b9d5-b4b83629e7ed",
      "name": "Daily CRON",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://api_dispatcher:8000/scan",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"domain\": \"juice_shop\"\n}",
        "options": {}
      },
      "id": "0ea1cf8c-a96b-4976-9a22-71a70014c1ba",
      "name": "Start scan",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        224,
        0
      ]
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "minutes"
      },
      "id": "8c3f5f54-2b6a-4c1f-8a91-1c9b2d8e5a01",
      "name": "Wait for scan",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        448,
        0
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=http://api_dispatcher:8000/scan/{{$node[\"Start scan\"].json[\"scan_id\"]}}",
        "options": {}
      },
      "id": "9d8a17b5-6c52-4f0a-a2c3-fa7e1c0e24b2",
      "name": "Check scan status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        672,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "completed-check",
              "leftValue": "={{$json[\"status\"]}}",
              "rightValue": "completed",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f3b8e1d4-91c2-4ec3-90f7-71d3b2c0a8a3",
      "name": "Is completed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        896,
        0
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=http://api_dispatcher:8000/results/{{$node[\"Start scan\"].json[\"scan_id\"]}}/report?format=json",
        "options": {}
      },
      "id": "4a52cd17-0b42-4d35-94e6-2e8a9da4bf90",
      "name": "Fetch JSON report",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1120,
        -120
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=http://api_dispatcher:8000/results/{{$node[\"Start scan\"].json[\"scan_id\"]}}/report?format=html",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "57c9a2b8-6f42-4f5a-b8d1-3a6f8c50e8d1",
      "name": "Fetch HTML report",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1120,
        120
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "sec-alerts",
          "mode": "name"
        },
        "text": "={{ $json.executive_summary.total_vulnerabilities === 0 ? ':white_check_mark: *Daily ASM scan \u2014 ' + $json.target_domain + '*\\nNo vulnerabilities detected. All systems safe.\\nScan ID: `' + $json.scan_id + '`' : ':rotating_light: *Daily ASM scan \u2014 ' + $json.target_domain + '*\\n*Findings:* ' + $json.executive_summary.total_vulnerabilities + ' \u00b7 *Critical:* ' + ($json.executive_summary.vulnerabilities_by_severity.critical || 0) + ' \u00b7 *High:* ' + ($json.executive_summary.vulnerabilities_by_severity.high || 0) + ' \u00b7 *Medium:* ' + ($json.executive_summary.vulnerabilities_by_severity.medium || 0) + ' \u00b7 *KEV:* ' + $json.executive_summary.kev_findings + '\\n*Compliance gaps:* ' + $json.executive_summary.compliance_standards_violated.length + ' (' + $json.executive_summary.compliance_standards_violated.slice(0,3).join(', ') + (($json.executive_summary.compliance_standards_violated.length > 3) ? '\u2026' : '') + ')\\n*Top risk:* ' + (($json.top_risks[0] && ($json.top_risks[0].name + ' on `' + $json.top_risks[0].asset + '`' + ($json.top_risks[0].cisa_kev_exploited ? ' [KEV]' : ''))) || 'n/a') + '\\n_Full report:_ http://localhost:8000/results/' + $json.scan_id + '/report?format=html' }}",
        "otherOptions": {}
      },
      "id": "8e1d9a55-b7c3-4f9e-91d2-3a8c4b6e7f01",
      "name": "Post daily summary to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        1344,
        -120
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fromEmail": "asm-bot@example.com",
        "toEmail": "grc@example.com",
        "subject": "=Weekly ASM Report \u2014 {{$node[\"Fetch JSON report\"].json[\"target_domain\"]}}",
        "html": "={{$node[\"Fetch HTML report\"].json[\"data\"]}}",
        "options": {}
      },
      "id": "62a47d9c-3b1e-4f08-9d6a-1f2b8c95a4b7",
      "name": "Email report to GRC",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2,
      "position": [
        1344,
        120
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "asm-alert",
        "options": {}
      },
      "id": "743d607d-a25e-4b20-be4d-a84bfbe8ea23",
      "name": "Critical-finding webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        0,
        384
      ]
    },
    {
      "parameters": {
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "sec-alerts",
          "mode": "name"
        },
        "text": "=:rotating_light: *{{ $json.body.severity.toUpperCase() }}* \u2014 {{ $json.body.name }}\nAsset: `{{ $json.body.asset }}`\nCVE: {{ $json.body.cve || \"n/a\" }}  KEV: {{ $json.body.is_kev }}\nScan: {{ $json.body.scan_id }}",
        "otherOptions": {}
      },
      "id": "4108eb11-e031-495e-9619-c2ca93f362d4",
      "name": "Post per-finding alert to Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [
        288,
        384
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Daily CRON": {
      "main": [
        [
          {
            "node": "Start scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start scan": {
      "main": [
        [
          {
            "node": "Wait for scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for scan": {
      "main": [
        [
          {
            "node": "Check scan status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check scan status": {
      "main": [
        [
          {
            "node": "Is completed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is completed?": {
      "main": [
        [
          {
            "node": "Fetch JSON report",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch HTML report",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait for scan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch JSON report": {
      "main": [
        [
          {
            "node": "Post daily summary to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch HTML report": {
      "main": [
        [
          {
            "node": "Email report to GRC",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Critical-finding webhook": {
      "main": [
        [
          {
            "node": "Post per-finding alert to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "tags": []
}