{# Defensive defaults block — placed BEFORE the render-mode gate per the E.1/E.2/E.3/1c.1/1c.2 pattern. Customer Brief's transform returns 18 keys (16 always + 2 mode-conditional). Defensive guards coerce non- mapping / non-iterable upstream values so the template never crashes on data drift. #} {% set summary = summary | default({}) %} {% if summary is string or summary is not mapping %}{% set summary = {} %}{% endif %} {% set triage_summary = triage_summary | default({}) %} {% if triage_summary is string or triage_summary is not mapping %}{% set triage_summary = {} %}{% endif %} {% set remediation_highlights = remediation_highlights | default({}) %} {% if remediation_highlights is string or remediation_highlights is not mapping %}{% set remediation_highlights = {} %}{% endif %} {% set sbom_stats = sbom_stats | default({}) %} {% if sbom_stats is string or sbom_stats is not mapping %}{% set sbom_stats = {} %}{% endif %} {% set scan_metadata = scan_metadata | default({}) %} {% if scan_metadata is string or scan_metadata is not mapping %}{% set scan_metadata = {} %}{% endif %} {% set severity_distribution = severity_distribution | default({}) %} {% if severity_distribution is string or severity_distribution is not mapping %}{% set severity_distribution = {} %}{% endif %} {% set reachability_summary = reachability_summary | default({}) %} {% if reachability_summary is string or reachability_summary is not mapping %}{% set reachability_summary = {} %}{% endif %} {% set exploit_maturity_summary = exploit_maturity_summary | default({}) %} {% if exploit_maturity_summary is string or exploit_maturity_summary is not mapping %}{% set exploit_maturity_summary = {} %}{% endif %} {% set top_findings = top_findings | default([]) %} {% if top_findings is mapping or top_findings is string or top_findings is not iterable %}{% set top_findings = [] %}{% endif %} {% set detailed_findings = detailed_findings | default([]) %} {% if detailed_findings is mapping or detailed_findings is string or detailed_findings is not iterable %}{% set detailed_findings = [] %}{% endif %} {% set component_risk_ranking = component_risk_ranking | default([]) %} {% if component_risk_ranking is mapping or component_risk_ranking is string or component_risk_ranking is not iterable %}{% set component_risk_ranking = [] %}{% endif %} {% set component_license_distribution = component_license_distribution | default([]) %} {% if component_license_distribution is mapping or component_license_distribution is string or component_license_distribution is not iterable %}{% set component_license_distribution = [] %}{% endif %} {% set top_security_risks = top_security_risks | default([]) %} {% if top_security_risks is mapping or top_security_risks is string or top_security_risks is not iterable %}{% set top_security_risks = [] %}{% endif %} {% set mode = mode | default('summary') %} {% set domain = domain | default('') %} {% set organization = organization | default('') %} {% set cover_image_b64 = cover_image_b64 | default('') %} {# components_missing_license must be int — the Component Licenses footnote does `> 0` comparison. Coerce defensively (round-1 multi-review M3-4 patch). #} {% set _cml_raw = components_missing_license | default(0) %} {% set components_missing_license = (_cml_raw | int) if _cml_raw is number or (_cml_raw is string and _cml_raw.isdigit()) else 0 %} {# generated_at default — keeps cover card / pdf-header / status_bar from rendering bare empty strings (round-1 multi-review M1-7 patch + round-2 M3-1 follow-up: `| default('—')` only fires on UNDEFINED, not on empty-string or None. Use `or '—'` so falsey strings + None also resolve to the visible fallback). #} {% set generated_at = generated_at or '—' %} {% import "_console_macros.html" as fs with context %} {# E1: single brand-resolution path — the configured logo (config.logo) resolved centrally to logo_path (full data-URI), bundled FS wordmark as fallback. Replaces the old logo_image_b64 raw-base64 PNG that IGNORED config.logo. fs.default_logo_data_uri() is the same bundled asset every other report falls back to. Computed AFTER the macro import so the fallback macro is in scope. #} {% set cb_logo = logo_path if (logo_path is defined and logo_path) else fs.default_logo_data_uri() %} {% if render_mode != 'fragment' %} {% if domain %}{% endif %} {% if mode == 'detailed' %}Customer Brief Detailed{% else %}Customer Brief{% endif %}{% if scan_metadata and scan_metadata.get('project_name') %} — {{ scan_metadata.get('project_name') }}{% endif %} {# ECharts library — replaces the pre-migration Chart.js stack. Only ONE chart in this recipe (license horizontal bar). The Risk Overview .cb-sev-bar + Triage Status .cb-triage-bar are SERVER-RENDERED HTML stacked bars (preserved verbatim with class rename only — see CSS below). #} {% 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-customer-brief / .fs-section-customer-brief-detailed, and re-emits at the top of the fragment. All hand-rolled classes use the .cb-* prefix. PDF-specific structures (@page rules, .cb-cover-page, .cb-intro-page, .cb-appendix markup with @media gates) are preserved from the pre-migration template — load-bearing for PDF parity (tests/test_pdf_parity.py). #} {% if render_mode != 'fragment' %} {# Unscoped page-box rules — kept out of the always-emitted style block so they don't bleed into fragment bundles (round-1 multi-review M1-3 patch). css_scoper.py does not prefix @page selectors, so emitting these in the fragment path would let customer-brief override the host document's page margins. #} {# IMPORTANT: customer_brief's is NOT class="console". Customer Brief is a PDF deliverable (Playwright pipeline); the @page rules + .cb-cover-page absolute-positioned cover image need a neutral body (no console-shell chrome bleeding into the PDF layout). Verified against Playwright PDF parity baseline. data-nav-category is preserved for the serve-mode sidebar. #} {% endif %} {# Build topbar meta items. Mode/Period/Organization gated on data. #} {% set cb_meta = [{"label": "Domain", "value": domain or "—"}] %} {% if scan_metadata.get('project_name') %} {% set _cb_scope = scan_metadata.get('project_name') %} {% if scan_metadata.get('version_name') %} {% set _cb_scope = _cb_scope ~ ' / v' ~ scan_metadata.get('version_name') %} {% endif %} {% set _ = cb_meta.append({"label": "Scope", "value": _cb_scope}) %} {% endif %} {% if scan_metadata.get('scan_date_range') %} {% set _ = cb_meta.append({"label": "Period", "value": scan_metadata.get('scan_date_range')}) %} {% endif %} {% if mode %} {# Human-readable Mode label (1c.1 lesson — never render raw enum). #} {% set _cb_mode_label = 'Detailed' if mode == 'detailed' else ('Summary' if mode == 'summary' else (mode | capitalize)) %} {% set _ = cb_meta.append({"label": "Mode", "value": _cb_mode_label}) %} {% endif %} {% if organization %} {% set _ = cb_meta.append({"label": "Organization", "value": organization}) %} {% endif %} {% set _ = cb_meta.append({"label": "Generated", "value": generated_at or "—"}) %} {% if render_mode != 'fragment' %} {# print_brand=false: Customer Brief has its own PDF cover + #pdf-header that carry the brand, so the shared print-only .print-brand block would duplicate it (E1). #} {{ fs.topbar( crumbs=["Finite State", "Customer Brief"], meta=cb_meta, controls=[], print_brand=false, ) }} {% endif %} {# PDF-only structures (cover page, pdf-header template, intro page) are gated on render_mode != 'fragment' so they don't bleed into compound fragment bundles (round-1 multi-review M1-2 patch). The standalone (HTML/PDF) path renders them; @media screen hides the cover/intro on screen, @media print shows them in PDF. #} {% if render_mode != 'fragment' %} {# PDF Cover Page — hidden on screen via @media screen, rendered in PDF print path. Embedded base64 cover image background + dark card with project info. Preserved verbatim from pre-migration template (class names renamed .cover-page → .cb-cover-page etc.). #}
{% if cb_logo %} {% endif %}

{% if mode == 'detailed' %}Security Risk Report{% else %}Security Risk Summary{% endif %}

Project Name:
{{ scan_metadata.get('project_name', '') }}
Version:
{{ scan_metadata.get('version_name', '') }}
Organization:
{{ organization if organization else scan_metadata.get('organization', '') }}
Published:
{{ generated_at }}
CONFIDENTIAL — This document contains proprietary security assessment information.
{# Chromium PDF header (extracted via document.querySelector('#pdf-header').innerHTML by PDFRenderer and passed to page.pdf(header_template=...)).