Template Syntax
Create your own DOCX templates: write a regular Word document, drop in placeholders where you want data, and NullReport fills it all in on export.
Cheat sheet
| Want to… | Syntax |
|---|---|
| Insert a value | {{title}} |
| Section content | {{sections.scope}} |
| Summary count | {{critical_count}} |
| Loop over findings | {{#findings}} … {{/findings}} |
| Number a finding | {{_num}} or {{_num | changeID:'FINDING'}} |
| Show conditionally | {{#if critical_count > 0}} … {{/if}} |
| Fallback value | {{assessor | d:'N/A'}} |
| Format a date | {{assessment_date | date:'long'}} |
| Force a page break | {{pageBreak}} |
| Bookmark / link to it | {{bookmark}} / {{ref:title}} |
When you type {{title}}, Word sometimes splits it into fragments behind the scenes ({{,
title, }}), usually if you pause, change formatting mid-placeholder, or paste part of it. The
placeholder then won't resolve. Fix: delete it and retype it in one go, or type it in a
plain-text editor and paste the whole thing in.
Placeholders
Wrap a variable name in double curly braces: {{variable_name}}.
Report
| Placeholder | Becomes |
|---|---|
{{title}} | Report title |
{{client}} | Client name |
{{status}} | Report status |
Metadata fields become snake_case (Assessment Date becomes {{assessment_date}}, Client
Contact becomes {{client_contact}}):
| Placeholder | Source |
|---|---|
{{assessment_date}} | Assessment Date |
{{report_version}} | Report Version |
{{assessor}} | Assessor |
Sections can be placed by name, with either form:
{{sections.executive_summary}}
{{section_scope}}
Section content is rich text from the editor, and NullReport converts it to native Word
formatting (bold, lists, tables, images, and all). The same applies to finding fields like
{{description}}.
Summary counts: {{total_findings}}, {{critical_count}}, {{high_count}},
{{medium_count}}, {{low_count}}, {{info_count}}.
Loops over findings
Everything between {{#findings}} and {{/findings}} repeats once per finding:
{{#findings}}
...finding placeholders...
{{/findings}}
Finding variables: {{title}}, {{severity}}, {{cvss_score}}, {{cvss_vector}},
{{description}}, {{details}}, {{impact}}, {{remediation}}, plus any custom field in
snake_case (Affected Systems becomes {{affected_systems}}).
Loop variables:
| Placeholder | Becomes | Example |
|---|---|---|
{{_num}} | Auto-incrementing number | 1, 2, 3… |
{{_index}} | Zero-based index | 0, 1, 2… |
{{_first}} | True on the first finding | true/false |
{{_last}} | True on the last finding | true/false |
If the loop markers sit inside a table row, the whole row duplicates per finding (the most common pattern). Inside a list item, the item duplicates:
| {{#findings}}{{_num}} | {{title}} | {{severity}} | {{cvss_score}}{{/findings}} |
Conditionals
Show or hide content with {{#if}} … {{/if}} (optionally {{else}}). The block renders when
the value is truthy (not zero, empty, or null):
{{#if critical_count}}
This report contains critical findings.
{{else}}
No critical findings were identified.
{{/if}}
Comparisons use ==, !=, >, >=, <, and <=:
{{#if severity == 'Critical'}} ... {{/if}}
{{#if cvss_score >= 9.0}} ... {{/if}}
{{#if total_findings < 5}} ... {{/if}}
Conditionals work inside loops with each finding's data:
{{#findings}}
{{title}}
{{#if cvss_score >= 9.0}}CVSS: {{cvss_score}} (Critical){{/if}}
{{/findings}}
Filters
Transform a value with the pipe |:
Assessor: {{assessor | d:'Not assigned'}}
| Filter | Example | Result |
|---|---|---|
upper | {{title | upper}} | SQL INJECTION |
lower | {{title | lower}} | sql injection |
title | {{name | title}} | John Doe |
capitalize | {{status | capitalize}} | Draft |
d / default | {{assessor | d:'N/A'}} | N/A (if empty) |
changeID | {{_num | changeID:'FINDING'}} | FINDING-001 |
Dates: {{assessment_date | date:'short'}} gives 2/20/2026, …'long' gives February 20,
2026, and …'iso' gives 2026-02-20. Chain filters left to right: {{assessor | d:'N/A' | upper}}.
Page breaks
{{pageBreak}} forces a new page. To page-break after every finding except the last:
{{#findings}}
{{_num}}. {{title}}
{{description}}
{{#if _last}}{{else}}{{pageBreak}}{{/if}}
{{/findings}}
Finding IDs
changeID prefixes and zero-pads finding numbers to three digits:
{{_num | changeID:'FINDING'}} → FINDING-001
{{_num | changeID:'F'}} → F-001
Bookmarks & cross-references
Link a summary table to each finding's details. Use {{bookmark}} inside the loop to mark where
a finding begins (it creates finding_1, finding_2, and so on, or pass a prefix with
{{bookmark:'finding_'}}), and {{ref:field}} to create a hyperlink to it ({{ref:'See details'}}
for static text):
## Summary
| ID | Finding | Severity |
|----|---------|----------|
{{#findings}}
| {{_num | changeID:'FINDING'}} | {{ref:title}} | {{severity}} |
{{/findings}}
{{pageBreak}}
## Detailed Findings
{{#findings}}
{{bookmark}}
### {{_num | changeID:'FINDING'}} - {{title}}
{{description}}
{{#if _last}}{{else}}{{pageBreak}}{{/if}}
{{/findings}}
Clicking a finding in the summary jumps Word to its details.
Images & rich text
Images pasted or uploaded in the editor are embedded automatically, with no placeholder
needed (PNG, JPEG, GIF, WebP). They scale to fit (max 6 inches), keep the width you set, and
respect left, center, or right alignment. All editor formatting (bold, headings, lists, tables,
code, blockquotes, links, text color, highlight) converts to native Word styling wherever a
rich-text field like {{description}} or {{sections.scope}} is used.
A placeholder for an empty field renders as nothing: blank space, no error, and no leftover
{{…}}. Placeholder styling comes from your Word formatting, so make {{title}} 24pt bold and
the title comes out 24pt bold. Placeholders work in headers and footers too.
Worked example
[ Cover ]
{{title}}
Prepared for: {{client}}
Date: {{assessment_date | date:'long'}} Version: {{report_version}}
[ Executive Summary ]
{{sections.executive_summary}}
[ Findings Summary ]
Total: {{total_findings}} Critical: {{critical_count}} | High: {{high_count}} | Medium: {{medium_count}} | Low: {{low_count}} | Info: {{info_count}}
[ Findings ]
{{#findings}}
{{_num | changeID:'FINDING'}}. {{title}}
Severity: {{severity}} CVSS: {{cvss_score}}
Description: {{description}}
Impact: {{impact}}
Remediation: {{remediation}}
{{#if _last}}{{else}}{{pageBreak}}{{/if}}
{{/findings}}
[ Recommendations ]
{{sections.recommendations}}
In the report editor, Export → Copy Template Syntax copies every variable available for that report, including your custom metadata and finding fields, to your clipboard.