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

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

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

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

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

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}, {#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}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

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} and the {order}/{findingId} assignment.

Setting{#findings} orderWithin each severity
Severity (default)Critical → InfoDatabase order
CVSSHighest CVSS firstBy CVSS within severity

{#findingsBySeverity} and {#findingsByCvss} always use their respective sort regardless of the tab setting.

On this page