Templating and Rich Text Fields

DOCX templates uploaded to Ghostwriter and rich text fields in Ghostwriter are both processed with Jinja 2, which will replace text from the document with data from the report. For example, you can write {{ title }} in a finding description and it will be replaced by the report title when generating a report, without having to edit it manually.

Jinja has extensive documentation on their web site, which is a good starting point. For Word DOCX files, Ghostwriter uses the python-docx-template library which adds a few extensions.

Available Variables

All templates have access to the report data. To view what that includes, select the "Generate exportable JSON" option from the Generate Report page. For example, the report title is available as {{ title }} and the project title through {{ project.title }}.

In addition, the rich text fields on a finding have an additional finding variable, which is a copy of the object from the findings array in the report.

Tag Prefixes

When inserting rich text fields, or when wanting to add list items or table rows in a loop, a prefix on the Jinja tag must be used. These tag prefixes work by replacing the prefix's corresponding element, to ensure that the inserted elements are properly nested. Because the element is replaced, you should not place anything else along side a prefixed tag.

Inserting Rich Text

To insert a rich text field inside of a paragraph in another rich text field or a DOCX template, use {{p rich_text_variable}}. This will replace the tag's paragraph with the paragraphs in the rich text field. For example:

Report Additional Info:

{{p extra_fields.additional_info}}


To generate multiple paragraphs in a loop:

{%p for finding in findings %}


{{p finding.description}}

{%p endfor %}

By specifying p on the for and endfor statements, the paragraphs they are in will be deleted, so that they don't cause extra newlines in the document.

To generate a list in a DOCX template, use the p prefix:

  • {%p for tag in tags %}

  • {{tag}}

  • {%p endfor %}

To generate a list in a rich text field, use the li prefix:

  • {%li for tag in tags %}

  • {{tag}}

  • {%li endfor %}

To generate a table in a DOCX template or rich text field, use the tr prefix:


{%tr for finding in findings %}



{%tr endfor %}

Just like with the p prefix, li will replace the list element and tr will replace the entire table row.

Functions and Filters

Ghostwriter provides the following extensions in rich text fields:

  • {{.evidence name}} or {{mk_evidence("name")}}: Inserts the evidence with the specified friendly name. Rich text fields on findings will search the finding's evidence; others search through the report evidence. Also creates a Word bookmark.

  • {{.caption}}, {{.caption name}} or {{mk_caption("name")}}: Creates a caption. If a name is specified, creates a Word bookmark with the specified name, which can also be referenced in {{.ref}} below. Any text in the same paragraph will be added to the caption.

  • {{.ref name}} or {{mk_ref("name")}}: Makes a reference to a Word bookmark, usually generated by {{.evidence}} or {{.caption}} above. The text will go to the bookmark when ctrl+clicked.

  • {% for finding in findings|filter_severity(["Critical", "High"]) %}: Filters findings by severity to those in the list.

  • {% for finding in findings|filter_type(["Network", "Web"]) %}: Filters findings by type to those in the list.

  • {{text|strip_html}}: Removes HTML tags from the text while adding newlines based on <p> and <br> tags.

  • {% for target in targets|compromised %}: Filters targets by those marked as compromised.

  • {{ start_date|add_days(1) }}: Adds an amount of business days to the date.

  • {{ start_date|format_datetime("Ymd") }}: Formats a date according to a PHP-style format string.

  • {{ findings|get_item(0) }}: Gets an item from a list or dictionary. Unlike foo.bar in Jinja, this forcibly uses the Python indexing operator, so it will not conflict with attributes.

  • {{ title|regex_search("a-z+") }}: Performs a regex search on a string, returning the match or None.

  • {% for finding in findings|filter_tags(["xss", "T1651"]) %}: Filters objects to include only ones that have any of the tags in the list.


If you need to escape Jinja syntax so that you can emit text like "{{" in your document, you have two options:

First, you can use {{ "{{" }}, {{ "{%" }}, etc., to emit a string literal. This is convenient for one-off replacements.

For larger blocks, you can use Jinja's {% raw %} ... {% endraw %} blocks. Content in the block will not be parsed as Jinja tags and will be emitted as-is.

Last updated