Vulnsy Docs

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

PrefixMeaningExample
{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

TagDescription
{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

TagDescription
{client.name}Company name
{client.contactName}Contact person
{client.contactEmail}Contact email
{client.contactPhone}Contact phone
{client.address}Address

Project Fields

TagDescription
{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

TagDescription
{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}

TagDescription
{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.

LoopSort 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.

LoopFilters 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.

TagDescription
{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 Severity or CVSS 3.1 (default)
  • 4.0 when the tab's sort order is CVSS 4.0
TagDescription
{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.

TagDescription
{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.

TagDescription
{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).

TagDescription
{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}

TagDescription
{type}url, ip, hostname, network, other
{value}The asset value
{description}Asset description

Tabs

Tab Loops

LoopContents
{#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

TagDescription
{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.

TagDescription
{~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

ConsultantEmailRole
{#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} orderWithin each severity{cvssScore} resolves to
Severity (default)Critical → InfoDatabase orderCVSS 3.1
CVSS 3.1Highest CVSS 3.1 firstBy CVSS 3.1 within severityCVSS 3.1
CVSS 4.0Highest CVSS 4.0 firstBy CVSS 4.0 within severityCVSS 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.

ConditionalTrue 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

On this page