VulnScout uses Jinja2 as its templating engine, which has great community support, documentation, and supports any text-based format (AsciiDoc, HTML, Markdown, CSV, plain text, etc.).
| Path | Purpose |
|---|---|
src/views/templates/ |
Built-in templates shipped with VulnScout. |
.vulnscout/templates/ |
Per-project host-side templates (mounted automatically if the directory exists). |
Pass the file path directly to --report — it stages and runs the template in one step:
./vulnscout --project demo --report /path/to/my-report.adocSet the GENERATE_DOCUMENTS environment variable to automatically build templates at the end of every scan. Multiple templates are comma-separated (values are trimmed):
./vulnscout config GENERATE_DOCUMENTS "summary.adoc, all_assessments.adoc"Generated files are written to the outputs directory (default: .vulnscout/outputs/).
All templates can also be run on-demand from the web interface using the export button in the toolbar.
| Variable | Type | Description |
|---|---|---|
vulnerabilities |
dict | All vulnerabilities found in the project, keyed by ID. Use vulnerabilities | as_list to get a list. |
packages |
dict | All packages found in the project, keyed by ID. Use packages | as_list to get a list. |
assessments |
dict | All assessments found in the project, keyed by ID. Use assessments | as_list to get a list. |
author |
string | Company name producing this document (from AUTHOR_NAME config). |
export_date |
string | Date of export as YYYY-MM-DD. |
scan_date |
string | Date and time of the last scan, or unknown date if unavailable. |
client_name |
string | Customer company name. Set via CLIENT_NAME config or overridden per-export from the web UI. May be empty. |
unfiltered_vulnerabilities |
dict | All vulnerabilities, bypassing any active export filter. Use | as_list to get a list. Always the full dataset. |
unfiltered_assessments |
dict | All assessments, bypassing any active export filter. Keyed by assessment ID. |
failed_vulns |
list[string] | List of vulnerability IDs that triggered the --match-condition expression. Empty when no condition was set or no vulnerability matched. Use unfiltered_vulnerabilities[vuln_id] to get the full object. |
projects |
dict | All projects, keyed by UUID. Use projects | as_list to get a list. Each value has the fields described in the Project Object section below. |
variants |
dict | All variants, keyed by UUID. Use variants | as_list to get a list. Each value has the fields described in the Variant Object section below. |
scans |
dict | All scans, keyed by UUID. Use scans | as_list to get a list. Each value has the fields described in the Scan Object section below. |
sbom_documents |
dict | All SBOM documents, keyed by UUID. Use sbom_documents | as_list to get a list. Each value has the fields described in the SBOM Document Object section below. |
Use env(name, default="") to access host environment variables in your templates.
For example, to fetch the value of the DISTRO environment variable:
{{ env("DISTRO") }}
{{ env("DISTRO", "unknown") }}
VulnScout will automatically scan your templates for env("...") patterns and pass the corresponding environment variable values from the host system.
When exporting, users can add filters to export only some vulnerabilities. To bypass these filters and access all vulnerabilities, use unfiltered_vulnerabilities instead of vulnerabilities and unfiltered_assessments instead of assessments. This is useful when producing a summary or showing the number of filtered-out vulnerabilities.
| Field | Type | Description |
|---|---|---|
id |
string | CVE / vulnerability ID. |
found_by |
list[string] | Tools that found the vulnerability (e.g. grype, yocto, spdx3). |
datasource |
string | Primary URL for the vulnerability. |
namespace |
string | Database name (e.g. nvd). |
aliases |
list[string] | Alternative IDs for this vulnerability. |
related_vulnerabilities |
list[string] | IDs of related vulnerabilities. |
urls |
list[string] | Additional reference URLs. |
description |
string | Description of the vulnerability. |
severity.severity |
string | Severity level: low, medium, high, critical, unknown. |
severity.min_score |
float | Minimum CVSS score (0–10). |
severity.max_score |
float | Maximum CVSS score (0–10). |
severity.cvss |
list[CVSS] | List of CVSS scoring objects (version, score, vector, author). |
epss.score |
float | EPSS probability score (0–1). |
epss.percentile |
float | EPSS rank relative to all CVEs (0–1). |
effort.optimistic |
string | Optimistic fix time estimate in ISO 8601 duration (e.g. PT5H). |
effort.likely |
string | Likely fix time estimate in ISO 8601 duration (e.g. P1D). |
effort.pessimistic |
string | Pessimistic fix time estimate in ISO 8601 duration (e.g. P2DT4H). |
packages |
list[string] | Packages affected by the vulnerability, in form name@version. |
published |
string | CVE publish date as ISO 8601, or empty if unknown. |
status |
string | Current status string (see Assessment object for possible values). |
simplified_status |
string | Simplified status: pending, affected, fixed, or ignored. |
last_assessment |
Assessment | Most recent assessment object (shorthand for assessments[0]). |
assessments |
list[Assessment] | All assessments, sorted most recent first. |
variant_ids |
list[string] | UUIDs of every variant in which this vulnerability has at least one assessment. |
assessments_by_variant |
dict[string, list[Assessment]] | Assessments grouped by variant ID. Key is the variant UUID, value is the list of assessments for that variant. |
| Field | Type | Description |
|---|---|---|
name |
string | Name of the package. |
version |
string | Version of the package. |
cpe |
list[string] | List of CPE identifiers. |
purl |
list[string] | List of PURL identifiers. |
sbom_documents |
dict[string, SBOM Document] | SBOM documents that list this package, keyed by UUID. |
variants |
list[string] | UUIDs of variants in which this package appears (derived from its SBOM documents). |
vulnerabilities |
dict[string, Vulnerability] | Vulnerabilities affecting this package, keyed by vulnerability ID. |
| Field | Type | Description |
|---|---|---|
id |
string | Assessment ID (UUID). |
vuln_id |
string | ID of the vulnerability this assessment affects. |
packages |
list[string] | List of packages concerned. |
timestamp |
string | Creation datetime in ISO 8601 format. |
last_update |
string | Last update datetime in ISO 8601 format. |
status |
string | Status of the assessment (see possible values below). |
simplified_status |
string | Simplified status: pending, affected, fixed, or ignored. |
status_notes |
string | Free-text note about the status. |
justification |
string | Justification for the status (see possible values below). |
impact_statement |
string | Text explaining why the vulnerability does not apply. |
responses |
list[string] | List of remediation responses (see possible values below). |
workaround |
string | Workaround description. |
workaround_timestamp |
string | Datetime of this workaround in ISO 8601 format. |
variant_id |
string | UUID of the variant this assessment belongs to, or empty. |
| Field | Type | Description |
|---|---|---|
id |
string | Project ID (UUID). |
name |
string | Name of the project. |
| Field | Type | Description |
|---|---|---|
id |
string | Variant ID (UUID). |
name |
string | Name of the variant (e.g. build configuration). |
project_id |
string | UUID of the project this variant belongs to. |
scans |
dict[string, Scan] | All scans belonging to this variant, keyed by UUID. |
sbom_documents |
dict[string, SBOM Document] | All SBOM documents belonging to this variant (across all its scans), keyed by UUID. |
packages |
dict[string, Package] | All packages found in this variant, keyed by name@version. |
assessments |
list[Assessment] | All assessments scoped to this variant. |
vulnerabilities |
dict[string, Vulnerability] | All vulnerabilities that have at least one assessment in this variant, keyed by vulnerability ID. |
| Field | Type | Description |
|---|---|---|
id |
string | Scan ID (UUID). |
description |
string | Description of the scan (may be empty). |
timestamp |
string | Scan datetime in ISO 8601 format. |
variant_id |
string | UUID of the variant this scan belongs to. |
variant |
Variant | The variant object this scan belongs to. |
sbom_documents |
dict[string, SBOM Document] | All SBOM documents produced by this scan, keyed by UUID. |
packages |
dict[string, Package] | All packages found across this scan's SBOM documents, keyed by name@version. |
| Field | Type | Description |
|---|---|---|
id |
string | SBOM document ID (UUID). |
path |
string | File path of the SBOM document. |
source_name |
string | Name of the source that produced this document. |
format |
string | Format of the document (e.g. spdx, cdx, openvex, yocto_cve_check). |
scan_id |
string | UUID of the scan this document belongs to. |
variant_id |
string | UUID of the variant this document belongs to (derived from its scan). |
scan |
Scan | The scan object this document belongs to. |
packages |
dict[string, Package] | All packages listed in this document, keyed by name@version. |
vulnerabilities |
dict[string, Vulnerability] | All vulnerabilities affecting any package in this document, keyed by vulnerability ID. |
| Value | Description | Filter |
|---|---|---|
in_triage, under_investigation |
Vulnerability found but presence not confirmed. Default status. | status_pending, status_active |
affected, exploitable |
Vulnerability confirmed and affecting the product. | status_affected, status_active |
fixed, resolved, resolved_with_pedigree |
Vulnerability is fixed and no longer exploitable. | status_fixed, status_inactive |
not_affected, false_positive |
Vulnerability is a false positive or not affecting us. | status_ignored, status_inactive |
component_not_presentvulnerable_code_not_presentvulnerable_code_not_in_execute_pathvulnerable_code_cannot_be_controlled_by_adversaryinline_mitigations_already_existcode_not_presentcode_not_reachablerequires_configurationrequires_dependencyrequires_environmentprotected_by_compilerprotected_at_runtimeprotected_at_perimeterprotected_by_mitigating_control
can_not_fixwill_not_fixupdaterollbackworkaround_available
In addition to Jinja built-in filters, the following custom filters are available.
| Filter | Description |
|---|---|
as_list |
Convert a dict to a list using .values(). |
limit(n) |
Limit the number of results to n (int). |
print_iso8601 |
Transform an ISO 8601 duration string into a human-readable format (e.g. P2DT4H → 2d 4h) or format a datetime string. |
| Filter | Description |
|---|---|
status(x) |
Keep only vulnerabilities with status in x (str or list of str). |
status_pending |
Shorthand for in_triage + under_investigation. |
status_affected |
Shorthand for affected + exploitable. |
status_fixed |
Shorthand for fixed + resolved + resolved_with_pedigree. |
status_ignored |
Shorthand for not_affected + false_positive. |
status_active |
status_pending + status_affected. |
status_inactive |
status_fixed + status_ignored. |
| Filter | Description |
|---|---|
severity(x) |
Keep only vulnerabilities with severity in x (str or list of str). |
epss_score(x) |
Keep only vulnerabilities with EPSS score ≥ x. x is a percentage in [0, 100]. |
Both last_assessment_date and filter_by_publish_date accept date expressions in the following formats:
| Format | Meaning |
|---|---|
'>2026-01-01' |
After this date (exclusive) |
'>=2026-01-01' |
After or on this date (inclusive) |
'<2026-01-01' |
Before this date (exclusive) |
'<=2026-01-01' |
Before or on this date (inclusive) |
'2026-01-01..2026-01-31' |
Between two dates (inclusive) |
'2026-01-01' |
Exact date match |
| Filter | Description |
|---|---|
last_assessment_date(x) |
Filter vulnerabilities by their last assessment date. |
filter_by_publish_date(x, include_unknown=False) |
Filter by CVE publish date. Pass include_unknown=True to also include vulnerabilities with no known publish date. |
Note for
filter_by_publish_date: Exact date match ignores time (hours, minutes, seconds). Wheninclude_unknown=True, also includes vulnerabilities without a published date.
| Filter | Description |
|---|---|
sort_by_epss |
Sort vulnerabilities by EPSS score, highest first. |
sort_by_effort |
Sort vulnerabilities by effort (effort.likely), most effort first. |
sort_by_last_modified |
Sort vulnerabilities by latest assessment date, most recent first. |
sort_by_scan_date |
Sort scans by timestamp, most recent first. Accepts a dict (keyed by ID) or a list. |
| Filter | Description |
|---|---|
filter_by_variant(variant_id) |
Keep only items that belong to the given variant. Works on assessments, scans, and vulnerabilities (matches against variant_ids). Accepts a dict or a list. |
filter_by_project(project_id) |
Keep only items whose project_id matches. Works on variants. Accepts a dict or a list. |
Get total count of active/open vulnerabilities:
{{ vulnerabilities | as_list | status_active | length }}
Get active vulnerability with highest EPSS score:
{{ vulnerabilities | as_list | status_active | sort_by_epss | first }}
Get vulnerabilities assessed after a specific date:
{{ vulnerabilities | as_list | last_assessment_date('>2026-01-01') | length }}
Get vulnerabilities assessed in January 2026:
{{ vulnerabilities | as_list | last_assessment_date('2026-01-01..2026-01-31') }}
Get critical vulnerabilities assessed recently (after or on 2026-01-15):
{{ vulnerabilities | as_list | severity('critical') | last_assessment_date('>=2026-01-15') | sort_by_last_modified }}
List all critical unresolved vulnerabilities, sorted by most recent assessment:
{% for vuln in vulnerabilities | as_list | severity('critical') | status_active | sort_by_last_modified %}
- {{ vuln.id }} (CVSS: {{ vuln.severity.max_score }})
{% endfor %}
Use an environment variable in a template:
Product: {{ env("PRODUCT_NAME", "unknown") }}
Version: {{ env("PRODUCT_VERSION", "unknown") }}
List all projects and their variants:
{% for project in projects | as_list %}
Project: {{ project.name }}
{% for variant in variants | filter_by_project(project.id) %}
- Variant: {{ variant.name }}
{% endfor %}
{% endfor %}
List most recent scans for a variant:
{% for scan in scans | filter_by_variant(variant_id) | sort_by_scan_date | limit(5) %}
- {{ scan.timestamp | print_iso8601 }}: {{ scan.description }}
{% endfor %}
List SBOM documents:
{% for doc in sbom_documents | as_list %}
- {{ doc.source_name }} ({{ doc.format }}): {{ doc.path }}
{% endfor %}
Every entity exposes its related entities as embedded fields, so you can traverse the data model in any direction.
The relationships are:
Project
└─ Variant (.scans, .sbom_documents, .packages, .vulnerabilities, .assessments)
└─ Scan (.sbom_documents, .packages, .variant)
└─ SBOM Document (.packages, .vulnerabilities, .scan)
└─ Package (.sbom_documents, .variants, .vulnerabilities)
└─ Vulnerability (.variant_ids, .assessments_by_variant)
Loop over vulnerabilities per variant:
{% for variant in variants | as_list %}
{{ variant.name }}
{% for vuln in variant.vulnerabilities.values() | sort_by_epss %}
- {{ vuln.id }} (EPSS: {{ vuln.epss.score }})
{% endfor %}
{% endfor %}
Loop over packages per SBOM document:
{% for doc in sbom_documents | as_list %}
{{ doc.source_name }} — {{ doc.path }}
{% for pkg_id, pkg in doc.packages.items() %}
- {{ pkg.name }} {{ pkg.version }}
{% endfor %}
{% endfor %}
Full traversal:
{% for project in projects | as_list %}
{% for variant in variants | filter_by_project(project.id) %}
{% for doc in variant.sbom_documents.values() %}
{% for vuln in doc.vulnerabilities.values() | status_active | sort_by_epss %}
{{ vuln.id }} in {{ doc.path }}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
Per-variant assessment breakdown for a single vulnerability:
{% for variant_id, assessments in vuln.assessments_by_variant.items() %}
Variant {{ variant_id }}: {{ assessments | first | attr('status') }}
{% endfor %}
Packages that appear in multiple variants:
{% for pkg_id, pkg in packages.items() %}
{% if pkg.variants | length > 1 %}
{{ pkg.name }} {{ pkg.version }} appears in {{ pkg.variants | length }} variants
{% endif %}
{% endfor %}
Most recent scan for a variant (using embedded .scans):
{% for variant in variants | as_list %}
{% set latest = variant.scans | sort_by_scan_date | first %}
{{ variant.name }}: last scanned {{ latest.timestamp | print_iso8601 }}
{% endfor %}