False Positive Analysis
Component identity + finding-level FP detection
{# 4 KPI cells via fs.kpi_cell. #}
{{ fs.kpi_cell("Open Findings", summary.get('total_open_findings', 0) if summary is mapping else 0) }}
{{ fs.kpi_cell(
"FP Candidates",
summary.get('fp_candidates_found', 0) if summary is mapping else 0,
dot=("low" if (summary is mapping and summary.get('fp_candidates_found', 0) > 0) else None),
) }}
{{ fs.kpi_cell(
"Mechanical Detections",
summary.get('mechanical_detections', 0) if summary is mapping else 0,
dot=("high" if (summary is mapping and summary.get('mechanical_detections', 0) > 0) else None),
) }}
{{ fs.kpi_cell(
"AI Detections",
summary.get('ai_detections', 0) if summary is mapping else 0,
dot=("info" if (summary is mapping and summary.get('ai_detections', 0) > 0) else None),
) }}
{# Component Identity Assertions section. Field names come from
the transform return at false_positive_analysis.py:806-816 —
likely_product, nvd_product, evidence, confidence (NOT
verdict/rationale). #}
{% if identity_assertions and identity_assertions | length > 0 %}
Component Identity Assertions
{{ identity_assertions | length }} component(s) detected as misidentified relative to NVD. Each block summarises the scanned vs. expected product and lists the per-CVE verdicts.
{% for a in identity_assertions %}
{% if a is mapping %}
{{ a.component_name }} {{ a.component_version }} —
scanned as {{ a.likely_product or '?' }}, NVD expected
{{ a.nvd_product or '?' }}
MISMATCHED
confidence: {{ a.confidence or 'medium' }}
Evidence: {{ a.evidence or '—' }}
{% if a.cve_verdicts and a.cve_verdicts | length > 0 %}
CVE
Verdict
Rationale
Recommended VEX
{% for cv in a.cve_verdicts %}
{% if cv is mapping %}
{{ cv.cve_id }}
{% if cv.verdict == 'does_not_apply' %}
does_not_apply
{% else %}
might_still_apply
{% endif %}
{{ cv.rationale or '—' }}
{% if cv.verdict == 'does_not_apply' %}
NOT_AFFECTED / CODE_NOT_PRESENT
{% else %}
Review individually
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{# Residual FP Candidates section. #}
Residual FP Candidates
Findings on components whose identity was confirmed or ambiguous, flagged as likely FP by finding-level applicability analysis.
{% if candidates and candidates | length > 0 %}
CVE
Component
Severity
Confidence
Signals
Reason / AI Rationale
Action
{% for row in candidates %}
{% if row is mapping %}
{{ row.cve_id or row.finding_id or '—' }}
{{ row.component_name or '—' }} {{ row.component_version or '' }}
{{ row.severity or 'UNKNOWN' }}
{{ row.fp_confidence or '—' }}
{# fp_signals can be string (transform's default — comma-joined
at false_positive_analysis.py:895) OR list (defensive — the
pre-migration template handles both). Preserve both branches. #}
{% if row.fp_signals %}
{% if row.fp_signals is string %}
{% for sig in row.fp_signals.split(',') %}
{{ sig | trim }}
{% endfor %}
{% else %}
{% for sig in row.fp_signals %}
{{ sig }}
{% endfor %}
{% endif %}
{% else %}
—
{% endif %}
{# primary_reason visible by default; AI verdict + rationale
collapsible via (pre-migration two-tier pattern). #}
{{ row.primary_reason or '—' }}
{% if row.ai_rationale %}
AI details
Verdict: {{ row.ai_verdict or '—' }}
{{ row.ai_rationale }}
{% endif %}
{{ row.recommended_action or 'Review' }}
{% endif %}
{% endfor %}
{% else %}
No residual FP candidates.
{% endif %}
{# Component Identity Prompts section (air-gapped --ai-prompts
workflow). Iterates `ai_component_prompts` (NOT
ai_triage_prompts) — false_positive_analysis.py builds
per-component identity prompts as the user-facing copy-paste
workflow. Each entry has `component` + `prompt` fields. #}
{% if ai_component_prompts and ai_component_prompts | length > 0 %}
Component Identity Prompts
{{ ai_component_prompts | length }} prompt(s) for offline LLM review. Paste each into an external model; if the answer is IDENTITY: mismatched, run the per-CVE applicability fan-out using the LIKELY_PRODUCT and NVD_PRODUCT values. These prompts are also written to False Positive Analysis_prompts.md alongside this report.
{% for p in ai_component_prompts %}
{% if p is mapping %}
{{ p.component }}
{{ p.prompt }}
{% if render_mode != 'fragment' %}
Copy
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{# Detection by Signal Type section. #}
{% if summary is mapping and summary.get('by_signal_type') is mapping %}
Detection by Signal Type
Signal Count
{% for signal, count in summary.by_signal_type.items() %}
{{ signal }}
{{ count }}
{% endfor %}
{% endif %}
{% if render_mode != 'fragment' %}
{{ fs.status_bar(items=[
"Recipe: False Positive Analysis",
"Open: " ~ ((summary.get('total_open_findings', 0) if summary is mapping else 0) | string),
"FP Candidates: " ~ ((summary.get('fp_candidates_found', 0) if summary is mapping else 0) | string),
generated_at | default(''),
], version='fs-report ' ~ (fs_report_version | default('dev'))) }}
{% endif %}
{% if render_mode != 'fragment' %}
{% include '_action_buttons.html' %}
{% endif %}