# GAP-VEX-006: Sample VEX decision notification templates # SPDX-License-Identifier: AGPL-3.0-or-later # # Usage: # 1. Copy to etc/notify-templates/vex-decision.yaml # 2. Customize templates per channel type # 3. Import via: stella notify template import vex-decision.yaml templates: # Email template for VEX decision notifications - key: vex.decision.changed channel_type: email locale: en-US render_mode: markdown description: "Notification when VEX decision status changes" body: | ## VEX Decision Changed: {{ vulnerability_id }} **Product:** {{ product.name }} ({{ product.version }}) **PURL:** `{{ product.purl }}` **Status Changed:** {{ previous_status }} → **{{ new_status }}** ### Reachability Evidence {% if reachability_evidence %} - **State:** {{ reachability_evidence.state }} - **Confidence:** {{ reachability_evidence.confidence | percent }} - **Graph Hash:** `{{ reachability_evidence.graph_hash }}` {% if reachability_evidence.call_paths | length > 0 %} #### Call Paths ({{ reachability_evidence.call_paths | length }}) {% for path in reachability_evidence.call_paths | slice(0, 3) %} - **{{ path.entry_point }}** → ... → **{{ path.vulnerable_function }}** (depth {{ path.depth }}) {% endfor %} {% if reachability_evidence.call_paths | length > 3 %} _(and {{ reachability_evidence.call_paths | length - 3 }} more paths)_ {% endif %} {% endif %} {% if reachability_evidence.runtime_hits | length > 0 %} #### Runtime Hits ({{ reachability_evidence.runtime_hits | length }}) | Function | Hits | Last Observed | |----------|------|---------------| {% for hit in reachability_evidence.runtime_hits | slice(0, 5) %} | {{ hit.function_name }} | {{ hit.hit_count }} | {{ hit.last_observed | date }} | {% endfor %} {% endif %} {% else %} _(No reachability evidence available)_ {% endif %} ### Signature {% if signature.signed %} - **Signed:** Yes - **Algorithm:** {{ signature.algorithm }} - **Key ID:** `{{ signature.key_id }}` - **DSSE Envelope:** `{{ signature.dsse_envelope_id }}` {% if signature.rekor_entry_id %} - **Rekor Entry:** [{{ signature.rekor_entry_id }}]({{ signature.rekor_url }}) {% endif %} {% else %} - **Signed:** No (unsigned decision) {% endif %} --- [View in StellaOps]({{ dashboard_url }}/vuln/{{ vulnerability_id }}) # Slack template for VEX decision notifications - key: vex.decision.changed channel_type: slack locale: en-US render_mode: slack_blocks description: "Slack notification for VEX decision changes" body: | { "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "VEX Decision Changed: {{ vulnerability_id }}" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Product:*\n{{ product.name }}" }, { "type": "mrkdwn", "text": "*Status:*\n{{ previous_status }} → *{{ new_status }}*" } ] }, {% if reachability_evidence %} { "type": "section", "text": { "type": "mrkdwn", "text": "*Reachability:* {{ reachability_evidence.state }} ({{ reachability_evidence.confidence | percent }} confidence)\n*Graph Hash:* `{{ reachability_evidence.graph_hash | truncate(16) }}...`" } }, {% if reachability_evidence.call_paths | length > 0 %} { "type": "section", "text": { "type": "mrkdwn", "text": "*Call Paths:* {{ reachability_evidence.call_paths | length }} found\n{% for path in reachability_evidence.call_paths | slice(0, 2) %}• {{ path.entry_point }} → {{ path.vulnerable_function }}\n{% endfor %}" } }, {% endif %} {% endif %} { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Details" }, "url": "{{ dashboard_url }}/vuln/{{ vulnerability_id }}" } ] } ] } # Teams template for VEX decision notifications - key: vex.decision.changed channel_type: teams locale: en-US render_mode: adaptive_card description: "Teams notification for VEX decision changes" body: | { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.4", "body": [ { "type": "TextBlock", "text": "VEX Decision Changed: {{ vulnerability_id }}", "weight": "Bolder", "size": "Large" }, { "type": "FactSet", "facts": [ { "title": "Product", "value": "{{ product.name }} {{ product.version }}" }, { "title": "PURL", "value": "{{ product.purl }}" }, { "title": "Status", "value": "{{ previous_status }} → {{ new_status }}" } {% if reachability_evidence %} ,{ "title": "Reachability", "value": "{{ reachability_evidence.state }} ({{ reachability_evidence.confidence | percent }})" } ,{ "title": "Call Paths", "value": "{{ reachability_evidence.call_paths | length }}" } ,{ "title": "Runtime Hits", "value": "{{ reachability_evidence.runtime_hits | length }}" } {% endif %} ] } ], "actions": [ { "type": "Action.OpenUrl", "title": "View in StellaOps", "url": "{{ dashboard_url }}/vuln/{{ vulnerability_id }}" } ] } # Webhook template for VEX decision notifications (JSON payload) - key: vex.decision.changed channel_type: webhook locale: en-US render_mode: json description: "Webhook payload for VEX decision changes" body: | { "event_type": "vex.decision.changed", "timestamp": "{{ timestamp | iso8601 }}", "vulnerability_id": "{{ vulnerability_id }}", "product": { "key": "{{ product.key }}", "name": "{{ product.name }}", "version": "{{ product.version }}", "purl": "{{ product.purl }}" }, "previous_status": "{{ previous_status }}", "new_status": "{{ new_status }}", "reachability_evidence": {% if reachability_evidence %}{ "state": "{{ reachability_evidence.state }}", "confidence": {{ reachability_evidence.confidence }}, "graph_hash": "{{ reachability_evidence.graph_hash }}", "graph_cas_uri": "{{ reachability_evidence.graph_cas_uri }}", "call_path_count": {{ reachability_evidence.call_paths | length }}, "runtime_hit_count": {{ reachability_evidence.runtime_hits | length }}, "dsse_envelope_id": "{{ reachability_evidence.dsse_envelope_id }}", "rekor_entry_id": "{{ reachability_evidence.rekor_entry_id }}" }{% else %}null{% endif %}, "signature": { "signed": {{ signature.signed | json }}, "algorithm": "{{ signature.algorithm }}", "key_id": "{{ signature.key_id }}", "dsse_envelope_id": "{{ signature.dsse_envelope_id }}", "rekor_entry_id": "{{ signature.rekor_entry_id }}" }, "tenant_id": "{{ tenant_id }}", "dashboard_url": "{{ dashboard_url }}/vuln/{{ vulnerability_id }}" }