{# Defensive defaults block — placed before the render-mode gate per C.1 round-2 multi-review M1-2. Mirrors B.1/B.2/C.1 templates. #} {% set portfolio_table_data = portfolio_table_data | default({"rows": [], "row_count": 0}) %} {% if not portfolio_table_data is mapping %}{% set portfolio_table_data = {"rows": [], "row_count": 0} %}{% endif %} {% set portfolio_rows = portfolio_table_data.get('rows', []) or [] %} {% set chart_data = chart_data | default({}) %} {% if not chart_data is mapping %}{% set chart_data = {} %}{% endif %} {% import "_console_macros.html" as fs with context %} {% if render_mode != 'fragment' %} {% if domain %}{% endif %} Component Vulnerability Analysis - Finite State Report {# ECharts library — replaces Chart.js v4.4.1 + Chart.js datalabels plugin from the pre-migration template. ECharts handles all chart types used here (bar + line dual-axis, scatter with custom symbol size). #} {% include "_design_system.html" %} {% include "_console_shell.html" %} {% include "_echarts_ready.html" %} {% endif %} {# Recipe-local styles — render in BOTH standalone and fragment modes so fragment_extractor (_STYLE_RE) collects them, scopes under .fs-section-component-vulnerability-analysis, and re-emits at the top of the fragment. #} {% if render_mode != 'fragment' %} {% endif %} {# Build topbar meta items conditionally. Use portfolio_rows | length for the count rather than metadata.transformed_count — the latter reflects the (empty) main DataFrame for CVA per report_engine.py:5700, not the actual portfolio table size. Round-1 multi-review M1-3 / M2-1 caught this. #} {% set cva_meta = [{"label": "Domain", "value": domain or "—"}] %} {% set _ = cva_meta.append({"label": "Components", "value": ((portfolio_rows | length) | string)}) %} {% if metadata and metadata.start_date and metadata.end_date %} {% set _ = cva_meta.append({"label": "Period", "value": (metadata.start_date | string) ~ " → " ~ (metadata.end_date | string)}) %} {% endif %} {% set _ = cva_meta.append({"label": "Generated", "value": generated_at or "—"}) %} {% if render_mode != 'fragment' %} {{ fs.topbar( crumbs=["Finite State", "Component Vulnerability Analysis"], meta=cva_meta, controls=[], ) }} {% endif %}

Component Vulnerability Analysis

Portfolio-level component risk analysis

{# Pareto Chart panel — top risk components by composite risk score with cumulative percentage on a secondary axis. Gate on labels length (not the whole dict's length, which counts keys, not rows — round-1 M1-11). #} {% if chart_data.pareto_chart is mapping and (chart_data.pareto_chart.get('labels', []) | length) > 0 %}
{{ fs.panel_head("Top risk components", meta="Pareto analysis") }}

Components sorted by composite risk score with cumulative risk percentage. Shows which components contribute disproportionately to total risk.

{% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('pareto_chart', '
Chart unavailable in fragment view
') | safe }}
{% else %}
{% endif %}
{% endif %} {# Bubble Matrix panel — risk vs scope visualization. Gate on data array length (not the dict's outer length). #} {% if chart_data.bubble_matrix is mapping and (chart_data.bubble_matrix.get('data', []) | length) > 0 %}
{{ fs.panel_head("Component risk vs project scope", meta="bubble size = finding count") }}

Bubble chart showing risk score vs number of affected projects. Bubble size represents finding count.

{% if render_mode == 'fragment' and not fragment_scripts_enabled %}
{{ server_svgs.get('bubble_matrix', '
Chart unavailable in fragment view
') | safe }}
{% else %}
{% endif %}
{% endif %} {# Executive Summary narrative — preserves the pre-migration prose verbatim. Static content; no template variables. #}

Executive Summary

The Component Vulnerability Analysis provides a strategic portfolio-level view of the most problematic components across your entire organization. This report identifies which component versions pose the highest risk and have the broadest impact, enabling portfolio-wide component risk prioritization.

Key Insights from Visualizations

Strategic Recommendations

{# Portfolio-Level Component Risk Data table. #} {% if portfolio_rows and (portfolio_rows | length) > 0 %}
{{ fs.panel_head("Portfolio-level component risk data", meta=((portfolio_rows | length) | string) ~ " component" ~ ("s" if (portfolio_rows | length) != 1 else "")) }}
{% for row in portfolio_rows %} {% if row is mapping %} {% set pcr = row.get('portfolio_composite_risk', 0) | int %} {% set nrs = row.get('normalized_risk_score', 0) | int %} {% endif %} {% endfor %}
Component Name Version Projects Affected Portfolio Composite Risk Normalized Risk Score Findings Count Has KEV Has Exploits
{{ row.get('name', '') }} {{ row.get('version', '') }} {{ row.get('project_count', 0) }} {{ pcr }} {{ nrs }} {{ row.get('findings_count', 0) }} {% if row.get('has_kev') %}🔒{% else %}{% endif %} {% if row.get('has_exploits') %}💥{% else %}{% endif %}
{% else %}

No portfolio data available.

{% endif %}
{% if render_mode != 'fragment' %} {{ fs.status_bar(items=[ "Recipe: Component Vulnerability Analysis", "Components: " ~ ((portfolio_rows | length) | string), generated_at | default(''), ], version='fs-report ' ~ (fs_report_version | default('dev'))) }} {% endif %} {% if render_mode != 'fragment' or fragment_scripts_enabled %} {% include '_action_buttons.html' %} {% endif %}