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.startDateUS} | Start date — "03/17/25" (MM/DD/YY) |
{project.endDateUS} | End date — "03/17/25" (MM/DD/YY) |
{project.startDateUK} | Start date — "17/03/25" (DD/MM/YY) |
{project.endDateUK} | End date — "17/03/25" (DD/MM/YY) |
Organization & Metadata
| Tag | Description |
|---|---|
{organization.name} | Organization name |
{metadata.generatedDate} | Generation date — "17th March 2025" |
{metadata.generatedDateUS} | Generation date — "03/17/25" (MM/DD/YY) |
{metadata.generatedDateUK} | Generation date — "17/03/25" (DD/MM/YY) |
{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 or CVSS) |
{#findingsBySeverity}...{/findingsBySeverity} | Always by severity (Critical → Info) |
{#findingsByCvss}...{/findingsByCvss} | Always by CVSS 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
| Tag | Description |
|---|---|
{cvssScore} | Overall score (e.g. "7.5") or "Not Set" |
{cvssVector} | Full vector string (e.g. "CVSS:3.1/AV:N/AC:L/…") |
{cvssSeverity} | Severity label computed from score ("Critical", "High", etc.) |
{#hasCvss}...{/hasCvss} | Conditional — true if CVSS score exists |
{cvssBaseScore} | Base score or "Not Set" |
{cvssTemporalScore} | Temporal score or "Not Set" |
{cvssEnvironmentalScore} | Environmental score or "Not Set" |
{cvssBaseSeverity} | Severity label for base score |
{cvssTemporalSeverity} | Severity label for temporal score |
{cvssEnvironmentalSeverity} | Severity label for 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)" |
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}, {#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} | Sorted by CVSS |
{#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} and the {order}/{findingId} assignment.
| Setting | {#findings} order | Within each severity |
|---|---|---|
| Severity (default) | Critical → Info | Database order |
| CVSS | Highest CVSS first | By CVSS within severity |
{#findingsBySeverity} and {#findingsByCvss} always use their respective sort regardless of the tab setting.