Syntax
Vulnotes templates use the LiquidJS template engine. This page covers the syntax basics you need to write templates.
Live debugger
A live LiquidJS debugger is available in the report preview. It lets you test your template expressions directly against the actual report context — variables, filters, loops — and see the rendered output in real-time.

Output
Use double curly braces to output a variable:
{{ report.title }}
{{ client.name }}
{{ stats.totalVulnerabilities }}You can chain filters with the pipe | character:
{{ report.title | uppercase }}
{{ vuln.cvss.score | score }}Conditionals
if / elsif / else
{% if stats.criticalCount > 0 %}
{{ stats.criticalCount }} critical vulnerabilities found.
{% elsif stats.highCount > 0 %}
{{ stats.highCount }} high vulnerabilities found.
{% else %}
No critical or high vulnerabilities found.
{% endif %}Comparison operators
| Operator | Description |
|---|---|
== | Equal |
!= | Not equal |
> | Greater than |
< | Less than |
>= | Greater or equal |
<= | Less or equal |
Logical operators
{% if stats.criticalCount > 0 and stats.highCount > 0 %}
Both critical and high vulnerabilities were identified.
{% endif %}
{% if stats.criticalCount == 0 or stats.highCount == 0 %}
No critical or no high vulnerabilities.
{% endif %}Truthy / falsy checks
{% if scope.description %}
{{ scope.description }}
{% endif %}unless
unless is the opposite of if:
{% unless stats.totalVulnerabilities == 0 %}
Vulnerabilities were identified during the assessment.
{% endunless %}case / when
Inside a loop, use the loop variable:
{% case vuln.severity %}
{% when 'Critical' %}
Immediate action required
{% when 'High' %}
Action required
{% when 'Medium' %}
Should be addressed
{% when 'Low' %}
Consider fixing
{% else %}
Informational
{% endcase %}Loops
for
Loop through arrays like vulnerabilities, scope entries, or contacts:
{% for vuln in vulnerabilities %}
{{ vuln.title }}
Severity: {{ vuln.severity }}
{% endfor %}Loop variables
Inside a for loop, you have access to:
| Variable | Description |
|---|---|
forloop.index | Current iteration (1-based) |
forloop.index0 | Current iteration (0-based) |
forloop.first | true on first iteration |
forloop.last | true on last iteration |
forloop.length | Total number of items |
{% for vuln in vulnerabilities %}
{{ forloop.index }}. {{ vuln.title }}
{% endfor %}Sorting before looping
{% for vuln in vulnerabilities | sort_by: 'cvss.score' %}
{{ vuln.title }} — {{ vuln.cvss.score | score }}
{% endfor %}limit
Restrict the loop to a maximum number of iterations:
{% for vuln in vulnerabilities limit: 3 %}
{{ vuln.title }}
{% endfor %}offset
Skip a number of items before starting the loop:
{% for vuln in vulnerabilities offset: 1 %}
{{ vuln.title }}
{% endfor %}Assign
Create local variables within a template:
{% assign total = stats.criticalCount | plus: stats.highCount %}
{{ total }} vulnerabilities rated Critical or High.{% assign has_critical = false %}
{% if stats.criticalCount > 0 %}
{% assign has_critical = true %}
{% endif %}Page breaks
Insert a page break in the PDF output:
{% pagebreak %}