{
  "id": "f2ZfY7xKhYArVH4V",
  "name": "Remediate security vulnerabilities with n8n and Port",
  "tags": [],
  "nodes": [
    {
      "id": "6ec161d3-a66e-4229-ae19-e705eeb248b6",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1744,
        320
      ],
      "parameters": {
        "path": "security/vulnerability",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1,
      "webhookDescription": "Triggered when a new vulnerability is detected"
    },
    {
      "id": "59fad1ae-bc92-4d4d-8977-176e8b5313be",
      "name": "Get Context From Port",
      "type": "CUSTOM.portApiAi",
      "position": [
        -1504,
        320
      ],
      "parameters": {
        "prompt": "=You are an assistant that provides contextual enrichment for vulnerabilities using the Port catalog as a knowledge base. A new security vulnerability has been detected.\nVulnerability Details:\n- Identifier: {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n- Description: {{ $('Webhook Trigger').item.json.body.description }}\n- Severity: {{ $('Webhook Trigger').item.json.body.severity }}\n- CVE: {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n- Package: {{ $('Webhook Trigger').item.json.body.package || 'N/A' }}\n\nUsing the Port catalog, return relevant contextual information. Specifically, retrieve:\n1. The affected service or repository\n2. Service owners and responsible team\n3. Environment (production/staging/dev)\n4. Service tier and SLA requirements\n5. Team's Slack channel\n6. Related dependencies\n7. Jira project key for security tickets\n\nYour response must be a single valid JSON object only, without any code block formatting, markdown, explanations, or extra text.\nDo not include ```json or ``` or any surrounding characters \u2014 just raw JSON.\n\nRequired output schema:\n{\n  \"service_name\": \"\",\n  \"repository\": \"\",\n  \"service_tier\": \"production|staging|development\",\n  \"sla_hours\": 0,\n  \"owners\": [],\n  \"slack_channel\": \"\",\n  \"environment\": \"\",\n  \"dependencies\": [],\n  \"jira_project_key\": \"\"\n}",
        "operation": "invokeAgent",
        "agentIdentifier": "context_retriever_agent"
      },
      "typeVersion": 1
    },
    {
      "id": "894a418a-0821-41af-b361-67d242e4e90b",
      "name": "OpenAI Remediation Plan",
      "type": "n8n-nodes-base.openAi",
      "position": [
        -1088,
        320
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "prompt": {
          "messages": [
            {
              "content": "=You are a security engineer. Analyze this vulnerability and create a remediation plan.\n\nVulnerability:\n- ID: {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n- Description: {{ $('Webhook Trigger').item.json.body.description }}\n- Severity: {{ $('Webhook Trigger').item.json.body.severity }}\n- CVE: {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n- Package: {{ $('Webhook Trigger').item.json.body.package || 'N/A' }}\n\nService Context from Port:\n{{ $('Get Context From Port').item.json.executionMessage }}\n\nYour response must be a single valid JSON object only, without any code block formatting, markdown, explanations, or extra text.\nDo not include ```json or ``` or any surrounding characters \u2014 just raw JSON.\n\nRequired output schema:\n{\n  \"summary\": \"Brief description of the vulnerability\",\n  \"impact\": \"Business impact assessment\",\n  \"remediation_steps\": [\"Step 1\", \"Step 2\"],\n  \"is_auto_fixable\": true or false,\n  \"fix_prompt\": \"Detailed prompt for a coding agent to fix this vulnerability\",\n  \"estimated_effort\": \"low|medium|high\"\n}"
            }
          ]
        },
        "options": {},
        "resource": "chat",
        "requestOptions": {}
      },
      "typeVersion": 1
    },
    {
      "id": "6c3608c6-5534-45a4-bb9e-df8887abf06a",
      "name": "Check Severity Level",
      "type": "n8n-nodes-base.switch",
      "position": [
        -784,
        320
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "value2": "critical"
            },
            {
              "value2": "high"
            },
            {
              "value2": ".*",
              "operation": "regex"
            }
          ]
        },
        "value1": "={{ $('Webhook Trigger').item.json.body.severity.toLowerCase() }}",
        "dataType": "string",
        "fallbackOutput": 2
      },
      "typeVersion": 2
    },
    {
      "id": "a8a13823-f8d6-422e-ba0e-2fa220cbf6d6",
      "name": "Create Critical Jira Ticket",
      "type": "n8n-nodes-base.jira",
      "position": [
        -512,
        272
      ],
      "parameters": {
        "project": {
          "__rl": true,
          "mode": "list",
          "value": "10000",
          "cachedResultName": "Port"
        },
        "summary": "=[CRITICAL] {{ $('Webhook Trigger').item.json.body.vulnerability_id }}: {{ $('Webhook Trigger').item.json.body.description.substring(0, 100) }}",
        "issueType": {
          "__rl": true,
          "mode": "list",
          "value": "10002",
          "cachedResultName": "Task"
        },
        "additionalFields": {
          "labels": [
            "security"
          ],
          "priority": {
            "__rl": true,
            "mode": "list",
            "value": "10001",
            "cachedResultName": "Now (Urgent)"
          },
          "description": "=*Security Vulnerability - CRITICAL*\n\n*Vulnerability Details*\n- ID: {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n- CVE: {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n- Severity: CRITICAL\n- Package: {{ $('Webhook Trigger').item.json.body.package || 'N/A' }}\n\n*Description*\n{{ $('Webhook Trigger').item.json.body.description }}\n\n*Affected Service*\n{{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return '- Service: ' + (parsed?.service_name || 'Unknown') + '\\n- Repository: ' + (parsed?.repository || 'Unknown') + '\\n- Environment: ' + (parsed?.environment || 'Unknown') + '\\n- Owners: ' + (parsed?.owners?.join(', ') || 'Unknown'); } catch(e) { return 'Context unavailable'; } })() }}\n\n*AI Remediation Plan*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.summary || 'See remediation steps'; } catch(e) { return 'Analysis pending'; } })() }}\n\n*Impact*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.impact || 'Impact assessment pending'; } catch(e) { return 'Impact assessment pending'; } })() }}\n\n*Remediation Steps*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.remediation_steps?.map((s, i) => (i+1) + '. ' + s).join('\\n') || 'Steps pending'; } catch(e) { return 'Steps pending'; } })() }}\n\n*SLA*\nThis vulnerability must be resolved within {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.sla_hours || 24; } catch(e) { return 24; } })() }} hours.\n\n---\n_Auto-generated by n8n + Port security workflow_"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7a9e901c-1312-4a85-b3df-b0412f9722bc",
      "name": "Create High Jira Ticket",
      "type": "n8n-nodes-base.jira",
      "position": [
        -512,
        416
      ],
      "parameters": {
        "project": {
          "__rl": true,
          "mode": "list",
          "value": "10000",
          "cachedResultName": "Port"
        },
        "summary": "=[HIGH] {{ $('Webhook Trigger').item.json.body.vulnerability_id }}: {{ $('Webhook Trigger').item.json.body.description.substring(0, 100) }}",
        "issueType": {
          "__rl": true,
          "mode": "list",
          "value": "10002",
          "cachedResultName": "Task"
        },
        "additionalFields": {
          "labels": [
            "security"
          ],
          "priority": {
            "__rl": true,
            "mode": "list",
            "value": "2",
            "cachedResultName": "High"
          },
          "description": "=*Security Vulnerability - HIGH*\n\n*Vulnerability Details*\n- ID: {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n- CVE: {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n- Severity: HIGH\n- Package: {{ $('Webhook Trigger').item.json.body.package || 'N/A' }}\n\n*Description*\n{{ $('Webhook Trigger').item.json.body.description }}\n\n*Affected Service*\n{{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return '- Service: ' + (parsed?.service_name || 'Unknown') + '\\n- Repository: ' + (parsed?.repository || 'Unknown') + '\\n- Environment: ' + (parsed?.environment || 'Unknown') + '\\n- Owners: ' + (parsed?.owners?.join(', ') || 'Unknown'); } catch(e) { return 'Context unavailable'; } })() }}\n\n*AI Remediation Plan*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.summary || 'See remediation steps'; } catch(e) { return 'Analysis pending'; } })() }}\n\n*Remediation Steps*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.remediation_steps?.map((s, i) => (i+1) + '. ' + s).join('\\n') || 'Steps pending'; } catch(e) { return 'Steps pending'; } })() }}\n\n---\n_Auto-generated by n8n + Port security workflow_"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6f4fe862-3748-49fd-b197-98874801c330",
      "name": "Create Medium/Low Jira Ticket",
      "type": "n8n-nodes-base.jira",
      "position": [
        -512,
        560
      ],
      "parameters": {
        "project": {
          "__rl": true,
          "mode": "list",
          "value": "10000",
          "cachedResultName": "Port"
        },
        "summary": "=[{{ $('Webhook Trigger').item.json.body.severity.toUpperCase() }}] {{ $('Webhook Trigger').item.json.body.vulnerability_id }}: {{ $('Webhook Trigger').item.json.body.description.substring(0, 100) }}",
        "issueType": {
          "__rl": true,
          "mode": "list",
          "value": "10002",
          "cachedResultName": "Task"
        },
        "additionalFields": {
          "labels": [
            "security"
          ],
          "priority": {
            "__rl": true,
            "mode": "list",
            "value": "4",
            "cachedResultName": "Low"
          },
          "description": "=*Security Vulnerability*\n\n*Vulnerability Details*\n- ID: {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n- CVE: {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n- Severity: {{ $('Webhook Trigger').item.json.body.severity }}\n- Package: {{ $('Webhook Trigger').item.json.body.package || 'N/A' }}\n\n*Description*\n{{ $('Webhook Trigger').item.json.body.description }}\n\n*Affected Service*\n{{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return '- Service: ' + (parsed?.service_name || 'Unknown') + '\\n- Repository: ' + (parsed?.repository || 'Unknown'); } catch(e) { return 'Context unavailable'; } })() }}\n\n*Remediation Steps*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.remediation_steps?.map((s, i) => (i+1) + '. ' + s).join('\\n') || 'Steps pending'; } catch(e) { return 'Steps pending'; } })() }}\n\n---\n_Auto-generated by n8n + Port security workflow_"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c14c83a9-e20b-44dc-bb5f-122b860d34e0",
      "name": "Is Auto-Fixable?",
      "type": "n8n-nodes-base.if",
      "position": [
        -208,
        256
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.is_auto_fixable === true || (ai.fix_prompt && ai.fix_prompt.length > 10); } catch(e) { return false; } })() }}",
              "value2": "={{ true }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7e272afb-5682-44b0-a5a6-e5f9bab00321",
      "name": "Trigger Fix via Port AI Agent",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        112,
        128
      ],
      "parameters": {
        "url": "https://api.getport.io/v1/actions/run_claude_code/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ properties: { service: (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.repository || 'default-organization/repository'; } catch(e) { return 'default-organization/repository'; } })(), prompt: (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); const vulnId = $('Webhook Trigger').item.json.body.vulnerability_id; const jiraKey = $('Create Critical Jira Ticket').item.json?.key || 'N/A'; return 'Fix vulnerability ' + vulnId + ': ' + (ai.fix_prompt || ai.summary || 'security vulnerability') + '. Reference Jira ticket: ' + jiraKey + '. Create a PR with the fix.'; } catch(e) { return 'Fix the security vulnerability and create a PR.'; } })() } }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "cee7f862-9878-419a-82e6-307fb017b833",
      "name": "Alert Critical to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        352,
        160
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *CRITICAL SECURITY VULNERABILITY DETECTED*\n\n*Vulnerability:* {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n*CVE:* {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n*Severity:* CRITICAL \ud83d\udd34\n\n*Affected Service:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.service_name || 'Unknown'; } catch(e) { return 'Unknown'; } })() }}\n*Environment:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.environment || 'Unknown'; } catch(e) { return 'Unknown'; } })() }}\n*Owners:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.owners?.join(', ') || 'Not specified'; } catch(e) { return 'Not specified'; } })() }}\n\n*Summary:*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.summary || 'See Jira ticket'; } catch(e) { return 'See Jira ticket'; } })() }}\n\n*Impact:*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.impact || 'See Jira ticket'; } catch(e) { return 'See Jira ticket'; } })() }}\n\n*Jira Ticket:* {{ $('Create Critical Jira Ticket').item.json?.key || 'Creating...' }}\n\n\ud83e\udd16 *Auto-Fix Status:* Triggered\n\n_SLA: Resolve within {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.sla_hours || 24; } catch(e) { return 24; } })() }} hours_",
        "channel": "={{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.slack_channel || '#security-alerts'; } catch(e) { return '#security-alerts'; } })() }}",
        "attachments": [],
        "otherOptions": {}
      },
      "typeVersion": 1
    },
    {
      "id": "7b6609b8-ccc5-46d6-a1ed-ef61b655393c",
      "name": "Alert Critical (Manual Fix)",
      "type": "n8n-nodes-base.slack",
      "position": [
        128,
        336
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *CRITICAL SECURITY VULNERABILITY - MANUAL FIX REQUIRED*\n\n*Vulnerability:* {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n*CVE:* {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n*Severity:* CRITICAL \ud83d\udd34\n\n*Affected Service:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.service_name || 'Unknown'; } catch(e) { return 'Unknown'; } })() }}\n*Owners:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.owners?.join(', ') || 'Not specified'; } catch(e) { return 'Not specified'; } })() }}\n\n*Summary:*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.summary || 'See Jira ticket'; } catch(e) { return 'See Jira ticket'; } })() }}\n\n*Remediation Steps:*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.remediation_steps?.map((s, i) => (i+1) + '. ' + s).join('\\n') || 'See Jira ticket'; } catch(e) { return 'See Jira ticket'; } })() }}\n\n*Jira Ticket:* {{ $('Create Critical Jira Ticket').item.json?.key || 'Creating...' }}\n\n\u26a0\ufe0f *This vulnerability requires manual remediation*\n\n_SLA: Resolve within {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.sla_hours || 24; } catch(e) { return 24; } })() }} hours_",
        "channel": "={{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.slack_channel || '#security-alerts'; } catch(e) { return '#security-alerts'; } })() }}",
        "attachments": [],
        "otherOptions": {}
      },
      "typeVersion": 1
    },
    {
      "id": "367c49c6-9c43-4dbf-98dd-23b8d2c325aa",
      "name": "Alert High to Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        -208,
        448
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f *HIGH SEVERITY VULNERABILITY DETECTED*\n\n*Vulnerability:* {{ $('Webhook Trigger').item.json.body.vulnerability_id }}\n*CVE:* {{ $('Webhook Trigger').item.json.body.cve || 'N/A' }}\n*Severity:* HIGH \ud83d\udfe0\n\n*Affected Service:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.service_name || 'Unknown'; } catch(e) { return 'Unknown'; } })() }}\n*Owners:* {{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.owners?.join(', ') || 'Not specified'; } catch(e) { return 'Not specified'; } })() }}\n\n*Summary:*\n{{ (() => { try { let content = $('OpenAI Remediation Plan').item.json.message?.content || '{}'; if (content.includes('```json')) { content = content.replace(/```json\\n?/g, '').replace(/```\\n?/g, ''); } const ai = JSON.parse(content); return ai.summary || 'See Jira ticket'; } catch(e) { return 'See Jira ticket'; } })() }}\n\n*Jira Ticket:* {{ $('Create High Jira Ticket').item.json?.key || 'Creating...' }}",
        "channel": "={{ (() => { try { const ctx = $('Get Context From Port').item.json.executionMessage; const parsed = typeof ctx === 'string' ? JSON.parse(ctx) : ctx; return parsed?.slack_channel || '#security-alerts'; } catch(e) { return '#security-alerts'; } })() }}",
        "attachments": [],
        "otherOptions": {}
      },
      "typeVersion": 1
    },
    {
      "id": "6fc1bd29-00b5-40a3-88ae-092a975efa57",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2320,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 680,
        "content": "## Remediate security vulnerabilities with n8n and Port\n\nThis workflow automates security vulnerability management from detection to remediation.\n\n**How it works**\n\n1. Webhook receives vulnerability alerts from your scanner\n2. Port enriches with service context, ownership, and SLA info\n3. OpenAI generates a remediation plan\n4. Jira ticket is created based on severity\n5. For critical issues, Claude Code can create a fix PR\n6. Team is notified via Slack\n\n### Prerequisites\n\n- Port account with services and ownership configured\n- Port AI agent (`context_retriever_agent`)\n- Claude Code action in Port\n- Jira project for security tickets\n- Slack workspace connected\n- OpenAI API key"
      },
      "typeVersion": 1
    },
    {
      "id": "a4279dd4-c9fe-4518-b2b9-26a285e62b3b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1792,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 380,
        "content": "### 1. Trigger & enrichment\n\n**Webhook Trigger**\nReceives POST requests from security scanners (Snyk, Wiz, SonarQube, etc.)\n\n**Get Context From Port**\nQueries Port's catalog to enrich the vulnerability with:\n- Service name & repository\n- Team ownership\n- Environment (prod/staging/dev)\n- SLA requirements\n- Slack channel for notifications\n- Jira project key"
      },
      "typeVersion": 1
    },
    {
      "id": "9dd27328-1ffb-4242-b52b-1496ed97059c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 280,
        "content": "### 2. AI analysis\n\n**OpenAI Remediation Plan**\nAnalyzes the vulnerability and generates:\n- Summary of the issue\n- Business impact assessment\n- Step-by-step remediation guide\n- Auto-fix determination\n- Fix prompt for Claude Code"
      },
      "typeVersion": 1
    },
    {
      "id": "929c7b74-5795-43e8-a35a-79af8f8fa391",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -912,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 420,
        "content": "### Severity routing & Jira tickets\n\n**Check Severity Level**\nRoutes to the appropriate path based on severity.\n\n**Jira Ticket Creation**\nCreates tickets with full context:\n\n- Critical: \"Now (Urgent)\" priority\n- High: \"High\" priority\n- Medium/Low: \"Low\" priority\n\nTickets include vulnerability details, affected service info from Port, and AI-generated remediation steps."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "e9e96fe4-0790-417c-a0a9-7020984930a0",
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Get Context From Port",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Auto-Fixable?": {
      "main": [
        [
          {
            "node": "Trigger Fix via Port AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Alert Critical (Manual Fix)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Severity Level": {
      "main": [
        [
          {
            "node": "Create Critical Jira Ticket",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create High Jira Ticket",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Medium/Low Jira Ticket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Context From Port": {
      "main": [
        [
          {
            "node": "OpenAI Remediation Plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create High Jira Ticket": {
      "main": [
        [
          {
            "node": "Alert High to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Remediation Plan": {
      "main": [
        [
          {
            "node": "Check Severity Level",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Critical Jira Ticket": {
      "main": [
        [
          {
            "node": "Is Auto-Fixable?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger Fix via Port AI Agent": {
      "main": [
        [
          {
            "node": "Alert Critical to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}