{ "schema_version": "2.0.0", "name": "RHEL CVE Remediation - Intelligent Patching", "description": "RHEL CVE Remediation - Intelligent Patching", "triggers": [ { "id": "trigger_manual", "type": "manual_trigger", "name": "CVE Alert", "parameters": { "input_schema": { "type": "object", "required": [ "host", "cve_id" ], "properties": { "host": { "type": "string", "default": "node1", "description": "Target hostname in AAP inventory (e.g. node1, node2, node3)" }, "cve_id": { "type": "string", "default": "", "description": "CVE identifier from Red Hat Lightspeed (e.g. CVE-2026-4631). Leave empty to let Lightspeed pick the most critical CVE." }, "lab_tag": { "type": "string", "default": "group=xfd48", "description": "Insights group tag (e.g. group=xfd48)" } } } }, "position": { "x": 0, "y": 300 } } ], "nodes": [ { "id": "triage_agent", "type": "agentic", "name": "AI Triage Agent", "parameters": { "prompt": "You are a CVE triage coordinator for a RHEL fleet. Gather two facts and decide a governance route.\n\nAlert:\n- Host: ${trigger.host}\n- CVE: ${trigger.cve_id}\n- Tag: ${trigger.lab_tag}\n\nStep 1 - Query Red Hat Lightspeed MCP:\nCall 'vulnerability__get_cve_systems' with cve='${trigger.cve_id}' and filter_='${trigger.host}', limit=1.\nCheck: is advisory_available=true?\n\nStep 2 - Query AAP Inventory MCP (NOT Lightspeed inventory):\nCall 'hosts_list' with search='${trigger.host}' to find the AAP host.\nThen call 'hosts_variable_data_retrieve' with the host ID to get the AAP host variables.\nRead the 'env' variable. Expected values: 'dev' or 'production'.\nThis is the authoritative source - do NOT use Lightspeed inventory tags for this.\n\nDecide route:\n- 'auto_patch': advisory_available=true AND env == 'dev'\n- 'approved_patch': advisory_available=true AND env == 'production'\n- 'investigate': host not found in CVE results, or advisory_available=false\n\nOutput ONLY valid JSON:\n{\n \"route\": \"auto_patch or approved_patch or investigate\",\n \"confidence\": 0.0,\n \"cve_id\": \"${trigger.cve_id}\",\n \"cve_severity\": \"\",\n \"cvss_score\": 0.0,\n \"exploit_available\": false,\n \"lightspeed_remediation_available\": false,\n \"host_env\": \"\",\n \"reasoning\": \"one sentence\"\n}", "response_schema": { "type": "object", "properties": { "route": { "type": "string" }, "cve_id": { "type": "string" }, "host_env": { "type": "string" }, "reasoning": { "type": "string" }, "confidence": { "type": "number" }, "cvss_score": { "type": "number" }, "cve_severity": { "type": "string" }, "exploit_available": { "type": "boolean" }, "lightspeed_remediation_available": { "type": "boolean" } } }, "tool_selections": [], "integration_connections": [], "tool_selection_strategy": "SELECTED" }, "position": { "x": 298.695652173913, "y": 318.26086956521743 } }, { "id": "route_switch", "type": "switch", "name": "Route Decision", "parameters": { "cases": [ { "port": "case_0", "label": "Auto-Patch (Dev)", "condition": "${triage_agent.result.content.route} == 'auto_patch'" }, { "port": "case_1", "label": "Approval Required (Prod)", "condition": "${triage_agent.result.content.route} == 'approved_patch'" }, { "port": "case_2", "label": "Investigate", "condition": "${triage_agent.result.content.route} == 'investigate'" } ], "default_port": "default" }, "position": { "x": 720, "y": 300 } }, { "id": "fetch_and_commit", "type": "aap_job_template", "name": "Fetch from Lightspeed & Commit to Gitea", "parameters": { "diff_mode": false, "extra_vars": { "cve_id": "${trigger.cve_id}", "lab_tag": "${trigger.lab_tag}", "target_host": "${trigger.host}" }, "job_template_name": "CVE - Fetch and Commit", "organization_name": "Default" }, "settings": {}, "position": { "x": 1076.0869565217392, "y": -69.99999999999999 } }, { "id": "deploy_script", "type": "aap_job_template", "name": "Sync & Deploy via AAP", "parameters": { "diff_mode": false, "extra_vars": { "cve_id": "${trigger.cve_id}", "target_host": "${trigger.host}", "playbook_filename": "${fetch_and_commit.artifacts.playbook_filename}" }, "job_template_name": "CVE - Sync and Deploy Remediation", "organization_name": "Default" }, "settings": {}, "position": { "x": 1442.0648489314663, "y": -68.56153279292558 } }, { "id": "approval_prod", "type": "approval", "name": "Approve Production Patch", "parameters": {}, "position": { "x": 1080, "y": 300 } }, { "id": "fetch_and_commit_prod", "type": "aap_job_template", "name": "Fetch from Lightspeed & Commit to Gitea", "parameters": { "diff_mode": false, "extra_vars": { "cve_id": "${trigger.cve_id}", "lab_tag": "${trigger.lab_tag}", "target_host": "${trigger.host}" }, "job_template_name": "CVE - Fetch and Commit", "organization_name": "Default" }, "settings": {}, "position": { "x": 1410.3861459100956, "y": 312.45836403831976 } }, { "id": "deploy_script_prod", "type": "aap_job_template", "name": "Sync & Deploy via AAP", "parameters": { "diff_mode": false, "extra_vars": { "cve_id": "${trigger.cve_id}", "target_host": "${trigger.host}", "playbook_filename": "${fetch_and_commit_prod.artifacts.playbook_filename}" }, "job_template_name": "CVE - Sync and Deploy Remediation", "organization_name": "Default" }, "settings": {}, "position": { "x": 1702.3728813559323, "y": 313.42372881355936 } }, { "id": "investigate_agent", "type": "agentic", "name": "Investigate - No Known Remediation", "parameters": { "prompt": "Red Hat Lightspeed has no actionable remediation for ${triage_agent.result.output.cve_id} on host '${trigger.host}'.\n\nGather context to help the operator decide next steps:\n\n1. Use Lightspeed MCP to get all available info about ${triage_agent.result.output.cve_id} - affected component, attack vector, impact, any Red Hat workarounds or mitigations.\n\n2. Use Ansible MCP to check if the affected component is actually installed on '${trigger.host}' and what version is running.\n\n3. Summarize: is the host actually vulnerable? What is the risk? What workarounds exist?\n\nOutput ONLY valid JSON:\n{\n \"host_actually_affected\": true,\n \"installed_component_version\": \"\",\n \"workaround_available\": false,\n \"workaround_description\": \"\",\n \"risk_summary\": \"\",\n \"recommended_action\": \"\"\n}", "response_schema": { "type": "object", "properties": { "risk_summary": { "type": "string" }, "recommended_action": { "type": "string" }, "workaround_available": { "type": "boolean" }, "host_actually_affected": { "type": "boolean" }, "workaround_description": { "type": "string" }, "installed_component_version": { "type": "string" } } }, "tool_selections": [], "integration_connections": [], "tool_selection_strategy": "SELECTED" }, "position": { "x": 1086.5217391304348, "y": 617.8260869565216 } }, { "id": "notify_investigate", "type": "aap_job_template", "name": "Notify Mattermost - CVE Investigation", "parameters": { "diff_mode": false, "job_template_name": "CVE - Notify Mattermost Investigation", "organization_name": "Default" }, "position": { "x": 1886.915254237288, "y": 643.3220338983051 } }, { "id": "parse_investigation", "type": "script", "name": "Parse Investigation Result", "parameters": { "code": "import os, json, re\nraw = os.getenv('RAW', '{}')\nr = {}\ntry:\n r = json.loads(raw)\nexcept Exception:\n pass\nif not r:\n match = re.search(r'```(?:json)?\\s*({.*?})\\s*```', raw, re.DOTALL)\n if match:\n try: r = json.loads(match.group(1))\n except Exception: pass\nif not r:\n start = raw.find('{')\n if start != -1:\n fragment = raw[start:]\n for attempt in [fragment, fragment + '}', fragment + '\"}']:\n try: r = json.loads(attempt); break\n except Exception: pass\nprint(json.dumps({\n 'host_actually_affected': str(r.get('host_actually_affected', 'unknown')),\n 'installed_version': str(r.get('installed_version', 'unknown'))[:300],\n 'cvss_vector': str(r.get('cvss_vector', 'N/A'))[:150],\n 'attack_vector': str(r.get('attack_vector', 'unknown'))[:150],\n 'exploitability': str(r.get('exploitability', 'unknown'))[:150],\n 'workaround_available': str(r.get('workaround_available', 'false')),\n 'workaround_steps': str(r.get('workaround_steps', ''))[:800],\n 'advisor_recommendations': str(r.get('advisor_recommendations', 'None found'))[:600],\n 'risk_summary': str(r.get('risk_summary', 'CVE not found - manual review required'))[:400],\n 'next_actions': str(r.get('next_actions', 'Contact Red Hat support'))[:400],\n 'estimated_fix_timeline': str(r.get('estimated_fix_timeline', 'Unknown'))[:150]\n}))", "language": "python", "environment": { "RAW": "${investigate_agent.result.content}" } }, "position": { "x": 1530.6440677966102, "y": 664.2033898305085 } }, { "id": "run_cve_remediation", "type": "aap_job_template", "name": "Run CVE Remediation", "parameters": { "diff_mode": false, "job_template_name": "${deploy_script.artifacts.template_name}", "organization_name": "Default" }, "position": { "x": 1769.4915254237287, "y": -69.15254237288136 } }, { "id": "run_cve_remediation_prod", "type": "aap_job_template", "name": "Run CVE Remediation", "parameters": { "diff_mode": false, "job_template_name": "${deploy_script_prod.artifacts.template_name}", "organization_name": "Default" }, "position": { "x": 1994.915254237288, "y": 313.4237288135593 } } ], "edges": [ { "from": "trigger_manual", "to": "triage_agent" }, { "from": "triage_agent", "to": "route_switch" }, { "from": "route_switch", "to": "fetch_and_commit", "from_port": "case_0" }, { "from": "fetch_and_commit", "to": "deploy_script" }, { "from": "route_switch", "to": "approval_prod", "from_port": "case_1" }, { "from": "approval_prod", "to": "fetch_and_commit_prod", "from_port": "approved" }, { "from": "fetch_and_commit_prod", "to": "deploy_script_prod" }, { "from": "route_switch", "to": "investigate_agent", "from_port": "case_2" }, { "from": "investigate_agent", "to": "parse_investigation" }, { "from": "parse_investigation", "to": "notify_investigate" }, { "from": "deploy_script", "to": "run_cve_remediation" }, { "from": "deploy_script_prod", "to": "run_cve_remediation_prod" } ] }