Filters
Filters transform values when outputting variables. Chain them with the pipe | character.
INFO
The examples below use vuln. as the loop variable (e.g. vuln.severity). This assumes you are inside a {% for vuln in vulnerabilities %} loop.
Date formatting
formatDate
Formats a date value.
{{ dates.startDate | formatDate: 'short' }} <!-- 01/15/2025 -->
{{ dates.startDate | formatDate: 'long' }} <!-- January 15, 2025 -->
{{ dates.startDate | formatDate: 'full' }} <!-- Wednesday, January 15, 2025 -->| Format | Output |
|---|---|
short | 01/15/2025 |
long | January 15, 2025 |
full | Wednesday, January 15, 2025 |
String manipulation
capitalize, uppercase, lowercase
{{ "hello world" | capitalize }} <!-- Hello world -->
{{ report.title | uppercase }} <!-- PENETRATION TEST REPORT -->
{{ report.title | lowercase }} <!-- critical -->Score formatting
score
Formats a numeric value to one decimal place. Returns 0.0 for null or undefined values.
{{ vuln.cvss.score | score }} <!-- 7.5 -->
{{ vuln.cvss.baseScore | score }}cvssMetric
Returns Not Defined for null or empty CVSS metric values.
{{ vuln.cvss.AV | cvssMetric }} <!-- Network -->
{{ vuln.cvss.E | cvssMetric }} <!-- Not Defined (if empty) -->Color mapping
These filters return hex color codes that can be used in inline styles.
severityColor / severityBackgroundColor
Map a severity string to a color. Use severityColor for text and severityBackgroundColor for backgrounds.
These are typically used in table cells where you can set the cell's color via the visual editor, or in inline styles:
{{ vuln.severity | severityColor }}
{{ vuln.severity | severityBackgroundColor }}textColor / backgroundColor
Map a numeric CVSS score to a color.
| Range | Severity |
|---|---|
| 9.0 – 10.0 | Critical |
| 7.0 – 8.9 | High |
| 4.0 – 6.9 | Medium |
| 0.1 – 3.9 | Low |
| 0 | None |
{{ vuln.cvss.score | backgroundColor }}
{{ vuln.cvss.score | textColor }}mapColor / mapBgColor
Map arbitrary values to custom colors. Format: 'key1:#hex1,key2:#hex2,_default:#hex'.
{{ vuln.remediationComplexity | mapColor: 'Easy:#22c55e,Medium:#eab308,Hard:#ef4444,_default:#6b7280' }}
{{ vuln.priority | mapBgColor: 'P1:#dc2626,P2:#ea580c,P3:#ca8a04,P4:#2563eb' }}Matching is case-insensitive. Use _default for a fallback color.
Value mapping
mapValue
Translate or map a value to a different string. Format: 'key1:value1,key2:value2,_default:fallback'.
{{ vuln.severity | mapValue: 'Critical:Critique,High:Élevé,Medium:Moyen,Low:Faible,Info:Information' }}
{{ vuln.remediationComplexity | mapValue: 'Easy:Facile,Medium:Moyen,Hard:Difficile,_default:Inconnu' }}Matching is case-insensitive. Returns the original value if no match is found and no _default is set.
Sorting
sort_by
Sort an array by a property. Supports nested properties with dot-notation.
<!-- Sort by CVSS score -->
{% for vuln in vulnerabilities | sort_by: 'cvss.score' %}
{{ vuln.title }} — {{ vuln.cvss.score | score }}
{% endfor %}
<!-- Sort alphabetically by title -->
{% for vuln in vulnerabilities | sort_by: 'title' %}
{{ vuln.title }}
{% endfor %}sort_multi
Sort by multiple keys with custom value ordering. Format: 'property1:value1|value2;property2:value1|value2'.
Semicolons separate sort keys, colons separate property from value order, pipes separate values.
<!-- Sort by severity (Critical first), then by CVSS score -->
{% for vuln in vulnerabilities | sort_multi: 'severity:Critical|High|Medium|Low|Info;cvss.score:desc' %}
{{ vuln.severity }} — {{ vuln.title }} ({{ vuln.cvss.score | score }})
{% endfor %}Filtering by vulnerability template
When a report uses more than one vulnerability template (for example, a Web template and a Mobile template on the same report), every finding still ends up in the same vulnerabilities array. Each finding carries a templateName field with the name of the vulnerability template it was created from, so you can split or filter the list inside your report template.
Loop only findings from one template
Use a Liquid if inside the loop to render a section per template:
<!-- Web findings -->
{% for vuln in vulnerabilities %}
{% if vuln.templateName == 'Web' %}
{{ forloop.index }}. {{ vuln.title }} — {{ vuln.severity }}
{{ vuln.description }}
{% endif %}
{% endfor %}
<!-- Mobile findings -->
{% for vuln in vulnerabilities %}
{% if vuln.templateName == 'Mobile' %}
{{ forloop.index }}. {{ vuln.title }} — {{ vuln.severity }}
{{ vuln.description }}
{% endif %}
{% endfor %}Pre-filter with the where filter
The standard Liquid where filter returns a sub-array, which keeps forloop.index and size accurate for each group:
{% assign web_vulns = vulnerabilities | where: 'templateName', 'Web' %}
{% assign mobile_vulns = vulnerabilities | where: 'templateName', 'Mobile' %}
Web findings: {{ web_vulns | size }}
Mobile findings: {{ mobile_vulns | size }}
{% for vuln in web_vulns %}
{{ forloop.index }}. {{ vuln.title }}
{% endfor %}You can chain it with sort_by or sort_multi to order each group independently:
{% assign web_vulns = vulnerabilities | where: 'templateName', 'Web' | sort_by: 'cvss.score' %}TIP
The name must match the vulnerability template name exactly (case-sensitive). If you rename a vulnerability template, also update any templateName comparisons in your report templates.
Standard Liquid filters
All standard LiquidJS filters are also available. Some commonly used ones:
| Filter | Description | Example |
|---|---|---|
size | Array length or string length | {{ vulnerabilities | size }} |
plus | Add a number | {{ stats.criticalCount | plus: stats.highCount }} |
minus | Subtract a number | {{ stats.totalVulnerabilities | minus: stats.informativeCount }} |
default | Fallback value if empty | {{ client.logo | default: '/placeholder.png' }} |
join | Join array items | {{ vuln.tags | join: ', ' }} |
replace | Replace text | {{ report.title | replace: 'Draft', 'Final' }} |
truncate | Truncate to length | {{ vuln.description | truncate: 100 }} |
strip_html | Remove HTML tags | {{ vuln.description | strip_html }} |
newline_to_br | Convert newlines to <br> | {{ text | newline_to_br }} |
