Template Tags
Complete reference for all template tags available in Vulnsy DOCX report exports.
Template tags are placeholders in your Word template that get replaced with real data on export.
Tag Syntax
| Prefix | Meaning | Example |
|---|---|---|
{tag} | Plain text value | {report.title} |
{~tag} | Rich HTML → Word formatted text (bold, lists, etc.) | {~description} |
{%tag} | Image (base64 data) | {%image} |
{#tag}...{/tag} | Loop (array) or conditional (boolean/number) | {#findings}...{/findings} |
{@tag} | Raw XML (advanced) | {@captionXml} |
Loop tags ({#tag}, {/tag}) and image tags ({%image}) must each be in their own paragraph. Do not place them inline with other text.
Report Fields
| Tag | Description |
|---|---|
{report.title} | Report title |
{~report.executiveSummary} | Executive summary (HTML → formatted Word text) |
{report.executiveSummary} | Executive summary (plain text) |
{~report.methodology} | Methodology (HTML → formatted Word text) |
{report.overallRisk} | Overall risk: critical, high, medium, low, informational, or Not Set |
{report.hasOverallRisk} | Boolean — use as conditional {#report.hasOverallRisk}...{/report.hasOverallRisk} |
{report.version} | Version number (defaults to "1.0") |
{report.status} | Status: draft, review, or final |
Dynamic Narrative Sections
Narrative tab sections become template tags based on their slugified title (lowercased, non-alphanumeric → underscore).
A section titled "Introduction" becomes:
{~report.introduction}— HTML formatted{report.introduction_plain}— plain text
A section titled "Technical Summary" becomes:
{~report.technical_summary}— HTML formatted{report.technical_summary_plain}— plain text
Scope tab sections follow the same pattern: {~report.scope} / {report.scope_plain}, {~report.limitations} / {report.limitations_plain}, etc.
Client Fields
| Tag | Description |
|---|---|
{client.name} | Company name |
{client.contactName} | Contact person |
{client.contactEmail} | Contact email |
{client.contactPhone} | Contact phone |
{client.address} | Address |
Project Fields
| Tag | Description |
|---|---|
{project.name} | Project name |
{project.description} | Description |
{project.type} | Type (web_app, infrastructure, mobile, cloud, api, iot) |
{project.status} | Status |
{project.startDate} | Start date — "17th March 2025" |
{project.endDate} | End date — "17th March 2025" |
{project.startMonth} | Start month — "March" |
{project.endMonth} | End month — "March" |
{project.startMonthYear} | Start month + year — "March 2025" |
{project.endMonthYear} | End month + year — "March 2025" |
{project.startDateUS} | Start date US long — "March 17th 2025" |
{project.endDateUS} | End date US long — "March 17th 2025" |
{project.startDateUS_short} | Start date — "03/17/2025" (MM/DD/YYYY) |
{project.endDateUS_short} | End date — "03/17/2025" (MM/DD/YYYY) |
{project.startDateUS_abbr} | Start date — "Mar/17/2025" |
{project.endDateUS_abbr} | End date — "Mar/17/2025" |
{project.startDateUK} | Start date — "17/03/2025" (DD/MM/YYYY) |
{project.endDateUK} | End date — "17/03/2025" (DD/MM/YYYY) |
{project.startDateUK_abbr} | Start date — "17/Mar/2025" |
{project.endDateUK_abbr} | End date — "17/Mar/2025" |
Organization & Metadata
| Tag | Description |
|---|---|
{organization.name} | Organization name |
{metadata.generatedDate} | Generation date — "17th March 2025" |
{metadata.generatedMonth} | Generation month — "March" |
{metadata.generatedMonthYear} | Generation month + year — "March 2025" |
{metadata.generatedDateUS} | Generation date US long — "March 17th 2025" |
{metadata.generatedDateUS_short} | Generation date — "03/17/2025" (MM/DD/YYYY) |
{metadata.generatedDateUS_abbr} | Generation date — "Mar/17/2025" |
{metadata.generatedDateUK} | Generation date — "17/03/2025" (DD/MM/YYYY) |
{metadata.generatedDateUK_abbr} | Generation date — "17/Mar/2025" |
{metadata.generatedBy} | Name of the user who exported |
Consultants
Loop: {#consultants}...{/consultants}
| Tag | Description |
|---|---|
{name} | Consultant name (falls back to email if name is empty) |
{email} | Email address |
{jobTitle} | Job title |
{isFirst} | Boolean — true for first consultant |
{isLast} | Boolean — true for last consultant |
{index} | 1-based position in list |
{separator} | Comma after all but last (, or empty) |
Pre-joined: {consultantNames}
All consultant names joined with commas — useful for a single table cell.
Findings
Top-Level Finding Loops
These contain ALL findings across all tabs.
| Loop | Sort Order |
|---|---|
{#findings}...{/findings} | Tab's configured sort order (severity, CVSS 3.1, or CVSS 4.0) |
{#findingsBySeverity}...{/findingsBySeverity} | Always by severity (Critical → Info) |
{#findingsByCvss}...{/findingsByCvss} | "Active" CVSS — follows the tab's sort (3.1 by default, 4.0 when sorted by CVSS 4.0) |
{#findingsByCvss3}...{/findingsByCvss3} | Always by CVSS 3.1 score (highest first) |
{#findingsByCvss4}...{/findingsByCvss4} | Always by CVSS 4.0 score (highest first) |
Severity-Filtered Loops
Only findings of that severity. Empty arrays are falsy — content is skipped when no findings match.
| Loop | Filters to |
|---|---|
{#criticalFindings}...{/criticalFindings} | Critical only |
{#highFindings}...{/highFindings} | High only |
{#mediumFindings}...{/mediumFindings} | Medium only |
{#lowFindings}...{/lowFindings} | Low only |
{#infoFindings}...{/infoFindings} | Informational only |
Finding Count
{findingCount} — total count. Use {#findingCount}...{/findingCount} as a conditional (0 is falsy, skips content).
Fields Inside Any Finding Loop
Available inside {#findings}, {#findingsBySeverity}, {#criticalFindings}, etc.
| Tag | Description |
|---|---|
{order} | Sequential number (1, 2, 3…) based on sort order |
{findingId} | Formatted reference ID, e.g. "REF-C1", "REF-H2" (prefix from tab settings) |
{title} | Finding title |
{~description} | Description (HTML → formatted Word text, with embedded pasted images) |
{description_plain} | Description (plain text) |
{~remediation} | Remediation (HTML → formatted Word text) |
{remediation_plain} | Remediation (plain text) |
{~evidenceNarrative} | Evidence narrative (HTML → formatted Word text) |
{evidenceNarrative_plain} | Evidence narrative (plain text) |
{~references} | References (HTML → formatted Word text) |
{severity} | critical, high, medium, low, info |
{status} | open, closed, accepted, etc. |
CVSS Fields
Findings can carry two independent CVSS records — v3.1 and v4.0 — and a finding may have either, both, or neither. The "main" {cvss*} tags follow the tab's sort order so template columns reflect whichever version the user picked. Explicit per-version tags are available when a column must be pinned to one version.
Active CVSS (follows the tab's sort toggle)
{cvssScore}, {cvssVector}, {cvssSeverity}, {hasCvss} resolve to:
- 3.1 when the tab's sort order is
SeverityorCVSS 3.1(default) - 4.0 when the tab's sort order is
CVSS 4.0
| Tag | Description |
|---|---|
{cvssScore} | Overall score for the active version, or "Not Set" |
{cvssVector} | Full vector string for the active version |
{cvssSeverity} | Severity label ("Critical", "High", …) for the active version |
{#hasCvss}...{/hasCvss} | Conditional — true if the active version has a score |
{activeCvssScore} | Alias of {cvssScore} |
{activeCvssVector} | Alias of {cvssVector} |
{activeCvssVersion} | "3.1" or "4.0" — which version is currently active |
Explicit CVSS v3.1
Always pinned to CVSS 3.1 regardless of sort order.
| Tag | Description |
|---|---|
{cvss3Score} | CVSS 3.1 overall score or "Not Set" |
{cvss3Vector} | CVSS 3.1 full vector string |
{cvss3Severity} | Severity label derived from the 3.1 score |
{#hasCvss3}...{/hasCvss3} | Conditional — true if a CVSS 3.1 score exists |
{cvssBaseScore} | CVSS 3.1 Base score or "Not Set" |
{cvssTemporalScore} | CVSS 3.1 Temporal score or "Not Set" |
{cvssEnvironmentalScore} | CVSS 3.1 Environmental score or "Not Set" |
{cvssBaseSeverity} | Severity label for the 3.1 base score |
{cvssTemporalSeverity} | Severity label for the 3.1 temporal score |
{cvssEnvironmentalSeverity} | Severity label for the 3.1 environmental score |
{#hasTemporalScore}...{/hasTemporalScore} | Conditional — true if temporal metrics differ from base |
{#hasEnvironmentalScore}...{/hasEnvironmentalScore} | Conditional — true if environmental metrics differ from base |
{cvssBaseCalc} | e.g. "AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N (3.7)" |
{cvssTemporalCalc} | e.g. "E:P/RL:O/RC:C (3.4)" |
{cvssEnvironmentalCalc} | e.g. "CR:M/IR:M/AR:M/MAV:N/… (3.4)" |
Explicit CVSS v4.0
Always pinned to CVSS 4.0 regardless of sort order.
| Tag | Description |
|---|---|
{cvss4Score} | CVSS 4.0 overall score or "Not Set" |
{cvss4Vector} | CVSS 4.0 full vector string (e.g. "CVSS:4.0/AV:N/AC:L/AT:N/…") |
{cvss4Severity} | Severity label derived from the 4.0 score |
{#hasCvss4}...{/hasCvss4} | Conditional — true if a CVSS 4.0 score exists |
{cvss4BaseScore} | CVSS 4.0 Base score or "Not Set" |
{cvss4ThreatScore} | CVSS 4.0 Threat score or "Not Set" (v4.0's Threat group replaces v3.1's Temporal) |
{cvss4EnvironmentalScore} | CVSS 4.0 Environmental score or "Not Set" |
{cvss4BaseSeverity} | Severity label for the 4.0 base score |
{cvss4ThreatSeverity} | Severity label for the 4.0 threat score |
{cvss4EnvironmentalSeverity} | Severity label for the 4.0 environmental score |
{#hasCvss4ThreatScore}...{/hasCvss4ThreatScore} | Conditional — true if threat metrics differ from base |
{#hasCvss4EnvironmentalScore}...{/hasCvss4EnvironmentalScore} | Conditional — true if environmental metrics differ from base |
Evidence Loop (nested inside findings)
{#evidence}...{/evidence} — only uploaded evidence (not pasted images).
| Tag | Description |
|---|---|
{fileName} | File name |
{description} | Description/caption |
{%image} | The image (base64 data) |
{figureNumber} | Sequential number across entire document |
{caption} | "Figure N - description" |
{@captionXml} | Pre-formatted Word XML with Caption style + SEQ field |
Affected Assets Loop (nested inside findings)
{#affectedAssets}...{/affectedAssets}
| Tag | Description |
|---|---|
{type} | url, ip, hostname, network, other |
{value} | The asset value |
{description} | Asset description |
Tabs
Tab Loops
| Loop | Contents |
|---|---|
{#tabs}...{/tabs} | All tabs in order |
{#allTabs}...{/allTabs} | Alias for {#tabs} |
{#report_tabs}...{/report_tabs} | Tabs with prefix "report" (including "report2", "report3") |
{#appsec_tabs}...{/appsec_tabs} | Tabs with prefix "appsec" (including "appsec2", "appsec3") |
{#<prefix>_tabs}...{/<prefix>_tabs} | Tabs grouped by any custom prefix |
Fields Inside Tab Loops
| Tag | Description |
|---|---|
{name} | Tab name |
{exportTagPrefix} | e.g. "report", "appsec" |
{overallRisk} | Tab's overall risk level or "Not Set" |
{#hasOverallRisk}...{/hasOverallRisk} | Conditional |
{#isCritical}...{/isCritical} | True if overallRisk === "critical" |
{#isHigh}...{/isHigh} | True if overallRisk === "high" |
{#isMedium}...{/isMedium} | True if overallRisk === "medium" |
{#isLow}...{/isLow} | True if overallRisk === "low" |
{#isInformational}...{/isInformational} | True if overallRisk === "informational" |
{findingCount} | Number of findings in this tab (use as conditional) |
Per-Tab Finding Loops
Inside {#tabs}, these iterate that tab's findings only:
{#findings}, {#findingsBySeverity}, {#findingsByCvss}, {#findingsByCvss3}, {#findingsByCvss4}, {#criticalFindings}, {#highFindings}, {#mediumFindings}, {#lowFindings}, {#infoFindings}
All finding fields from the tables above are available inside these loops.
Per-Tab Narrative Sections
Inside {#tabs}, each tab's narrative sections are available as slugified tags:
{~introduction},{introduction_plain}{~technical_summary},{technical_summary_plain}{~scope}, etc.
Prefix-Scoped Tags
When a tab has a non-"report" prefix (e.g. "appsec"), its data is also available at the top level under that prefix. Useful for templates that don't use tab loops.
| Tag | Description |
|---|---|
{~appsec.introduction} | That tab's "Introduction" narrative (HTML) |
{appsec.introduction_plain} | Plain text version |
{appsec.overallRisk} | That tab's overall risk |
{#appsec.hasOverallRisk}...{/appsec.hasOverallRisk} | Conditional |
{#appsec.findings}...{/appsec.findings} | That tab's findings (tab sort order) |
{#appsec.findingsBySeverity}...{/appsec.findingsBySeverity} | Sorted by severity |
{#appsec.findingsByCvss}...{/appsec.findingsByCvss} | "Active" CVSS — follows that tab's sort (3.1 by default, 4.0 when sorted by CVSS 4.0) |
{#appsec.findingsByCvss3}...{/appsec.findingsByCvss3} | Always sorted by CVSS 3.1 |
{#appsec.findingsByCvss4}...{/appsec.findingsByCvss4} | Always sorted by CVSS 4.0 |
{#appsec.criticalFindings}...{/appsec.criticalFindings} | Critical only |
{#appsec.highFindings}...{/appsec.highFindings} | High only |
{#appsec.mediumFindings}...{/appsec.mediumFindings} | Medium only |
{#appsec.lowFindings}...{/appsec.lowFindings} | Low only |
{#appsec.infoFindings}...{/appsec.infoFindings} | Info only |
{appsec.findingCount} | Finding count for that tab |
Replace appsec with any custom prefix (network, mobile, etc.). When the prefix is report (default), these go under report.*.
Template Patterns
Simple Report
{report.title}
Client: {client.name}
Date: {project.startDate} - {project.endDate}
{~report.executiveSummary}
{#findings}
{findingId} - {title} ({severity})
CVSS: {cvssScore}
{~description}
{~remediation}
{#evidence}
{%image}
{@captionXml}
{/evidence}
{/findings}Tab-Based with Severity Sections
{#tabs}
{#findingCount}
{name} Technical Findings
{#criticalFindings}
{title} - {cvssScore}
{~description}
{~remediation}
{/criticalFindings}
{#highFindings}
{title} - {cvssScore}
{~description}
{~remediation}
{/highFindings}
{#mediumFindings}
{title} - {cvssScore}
{/mediumFindings}
{#lowFindings}
{title} - {cvssScore}
{/lowFindings}
{#infoFindings}
{title} - {cvssScore}
{/infoFindings}
{/findingCount}
{/tabs}Put {#findingCount} around tab content to skip tabs with no findings. Put page breaks INSIDE severity loops so they're skipped when empty.
CVSS-Ordered Findings
{#findingsByCvss}
{order}. {title}
Severity: {severity} | CVSS: {cvssScore} ({cvssSeverity})
{#hasCvss}
Base: {cvssBaseScore} | Temporal: {cvssTemporalScore} | Environmental: {cvssEnvironmentalScore}
Vector: {cvssBaseCalc}
{/hasCvss}
{~description}
{/findingsByCvss}Prefix-Scoped (Fixed Tabs, No Loop)
AppSec Assessment
{~appsec.introduction}
{#appsec.criticalFindings}
{title} - {cvssScore}
{/appsec.criticalFindings}
{#appsec.highFindings}
{title} - {cvssScore}
{/appsec.highFindings}Consultants Table
| Consultant | Role | |
|---|---|---|
{#consultants}{name} | {email} | {jobTitle}{/consultants} |
Or as a single string: {consultantNames}
Finding Sort Order
The tab's sort order setting controls the order of {#findings}, the {order} / {findingId} assignment, and the active-version {cvssScore} / {cvssVector} / {cvssSeverity} tags.
| Setting | {#findings} order | Within each severity | {cvssScore} resolves to |
|---|---|---|---|
| Severity (default) | Critical → Info | Database order | CVSS 3.1 |
| CVSS 3.1 | Highest CVSS 3.1 first | By CVSS 3.1 within severity | CVSS 3.1 |
| CVSS 4.0 | Highest CVSS 4.0 first | By CVSS 4.0 within severity | CVSS 4.0 |
{#findingsBySeverity}, {#findingsByCvss3}, and {#findingsByCvss4} always use their respective sort regardless of the tab setting. {#findingsByCvss} follows the active CVSS version (same logic as {cvssScore}).
Sort Conditionals
Use these inside a finding loop or at report / tab scope to render different table layouts per sort order. The generic {#isSortedByCvss} is TRUE for either CVSS version so older templates keep rendering when users toggle to 4.0.
| Conditional | True when |
|---|---|
{#isSortedBySeverity}...{/isSortedBySeverity} | Sort is Severity |
{#isSortedByCvss}...{/isSortedByCvss} | Sort is CVSS 3.1 or CVSS 4.0 |
{#isSortedByCvss3}...{/isSortedByCvss3} | Sort is CVSS 3.1 specifically |
{#isSortedByCvss4}...{/isSortedByCvss4} | Sort is CVSS 4.0 specifically |
{sortOrder} | Raw value: severity, cvss (= 3.1), or cvss4 |