Introducing the Word template variables
{% ... %}
{% if some_variable %}
{{ ... }}
{{ client.name }}
... |filter ...
{{ client.name|title }}
<
and >
(e.g., replace the character with %gt;
). If you want to check if something is greater or less than a value, use Jinja2’s gt()
and lt()
tests.
https://jinja.palletsprojects.com/en/stable/templates/#jinja-tests.gt
https://jinja.palletsprojects.com/en/stable/templates/#jinja-tests.lt
You can freely use <
or >
if you use it inside your report template (not a text field inside Ghostwriter).
The official Jinja2 documentation contains all the information you need to get started using its more advanced features.
There are also various considerations covered in the official Jinja2 documentation, such as whitespace control and escaping.
Template Designer Documentation
All of Ghostwriter’s expressions and statements should be wrapped in curly braces with one space to either side ({{ client.name }}
or {% if ... %}
) – unless otherwise noted.
If you do not include the spaces, Jinja2 will not recognize the expression as valid and will ignore it.
If you ever need to include double curly braces or Jinja2 code inside a report and you do not want it to be rendered, you can escape your text in a couple of ways.
One option is the {% raw %}{% endraw %}
block. Another is using Jinja2 ‘s “literal variable delimiter” ({{
) inside a variable expression (e.g., {{ '{{' }}
).
https://jinja.palletsprojects.com/en/3.0.x/templates/#escaping
if
block to check a value to determine the content or formatting. Conditional blocks are powerful when combined with things like your custom extra fields.
Conditional blocks can be written in a couple of different ways. The simplest will be familiar to anyone who has written a script in a language like Python:
cellbg
, creating tables, and other functionality below!Expression | Description |
---|---|
capitalize(string) | Capitalize the first character and convert the rest to lowercase |
lower(string) | Convert a value to all lowercase |
replace(string, old, new) | Replace the old string (a substring of the first argument) with a new string |
title(string) | Return a titlecased string |
trim(value, chars=None) | Strip leading and trailing characters (default is whitespace) |
unique(value, case_sensitive=False) | Return a list of unique items from an iterable |
upper(string) | Convert a value to all uppercase |
sort(iterable, reverse=False, case_sensitive=False, attribute=None) | Sort an iterable with Python’s sorted() . More info |
dictsort(mapping, case_sensitive=False, by="key", reverse=False) | Like sort but accepts a mapping of key and value pairs to yield a dictionary. More info |
Expression | Description |
---|---|
report_date | [String ] Full date the report was generated (localized based on server settings) |
project | [Dict ] All information about the project |
client | [Dict ] All information about the project’s client |
team | [Dict ] All team information (individuals assigned to the project) |
objectives | [Dict ] All objectives information |
targets | [Dict ] All project targets |
scope | [Dict ] All project scope lists |
infrastructure | [Dict ] All project infrastructure information |
logs | [Dict ] All activity logs and related entries from the project |
findings | [Dict ] All information about a project’s findings |
observations | [Dict ] All information about a project’s observations |
docx_template | [Dict ] All information about the selected DOCX template |
pptx_template | [Dict ] All information about the selected PPTX template |
company | [Dict ] All information about your company (configured in the admin panel) |
title | [String ] The report’s title set in Ghostwriter |
complete | [Bool ] Value indicating if the report has been marked as complete |
archived | [Bool ] Value indicating if the report has been marked as archived |
delivered | [Bool ] Value indicating if the report has been marked as delivered |
totals | [Dict ] Various sums and counts of different project-related values (e.g., total findings, objectives, and targets) |
project
key has separate values for the day, month, and year the project started and ended. Use these to assemble your own date or date range formats if you need to represent a date differently or only want part of the date.short_name
value set, Ghostwriter will replace references to client.short_name
with the client’s full name.{{ finding.description }}
in the template. It’s unlikely you would want that.
Jinja2’s striptags
filter can help, but it removes all HTML without preserving new lines. Ghostwriter’s custom strip_html
filter will strip the tags and preserve newlines, but the output will still be all plaintext. You must re-apply character and paragraph styles, font changes, and other options. Your evidence files will also appear as text placeholders.
To get what you see in the WYSIWYG editor in your Word document, add _rt
(for rich text) to the attribute’s name, use the p
tag (see Ghostwriter Tags below). The above example becomes:
severity_rt
attribute. You don’t style this text in the WYSIWYG editor. Ghostwriter creates a rich text version of your severity category that is colored using your configured color code.
The severity_rt
attribute only styles the color of the text run so that you can apply a paragraph style to it directly in your Word template. Use it with the r
tag (for a run) like so:
{{p findings_subdoc }}
Tag | Description |
---|---|
r | Text run |
p | New paragraph |
tr | Table row |
tc | Table column |
python-docx-template
, do not use {%p
, {%tr
, {%tc
or {%r
twice in the same paragraph, row, column, or run.Bad:Statement | Description |
---|---|
{% cellbg color_var %} | Color a table cell where color_var is a hex value without the # |
{% colspan some_number %} | Span a table cell over a some_number of columns |
Filter | Usage |
---|---|
filter_severity(list) | Accepts the findings variable and filters it with a list of severities. Example: This statement loops over only findings rated as High or Medium severity: {% for x in findings | filter_severity(["High", "Medium"]) %} |
strip_html(string) | Accepts HTML strings and strips all tags. |
compromised(targets) | Accepts targets value and filters it to only include hosts marked as compromised. |
filter_type(list) | Accepts the findings variable and filters it with a list of categories. Example: This statement loops over only findings with the type Network: {% for x in findings | filter_type(["Network"]) %} |
add_days(date, days) | Provide a date and a number of days (integer) to add or subtract. Use negative numbers for subtraction. Examples: "February 1, 2025" | add_days(-10) and {{ project.start_date }} | add_days(-10) return “2025 Jan 20” (where project.start_date : 2025 Feb 01) |
format_datetime(date, format_str) | Provide a date and a format string. Use Django date format strings. Examples: "February 1, 2025" | format_datetime("N j, Y") and {{ project.start_date }} | format_datetime("N j, Y") return “Feb. 1, 2025” (where project.start_date : 2025 Feb 01) |
get_item(list, index) | Provide a list and an index to retrieve the list item at that index. Example: ["ghostwriter", "report", "ghost"] | get_item(0) returns ghostwriter |
filter_tags(list, allowlist) | Accepts a list of objects (e.g., findings ) and filters it with a list of tags. Example: This statement loops over only findings tagged with xss : {% for x in findings | filter_tags(["xss"]) %} |
regex_search(text, regex) | Perform a search with a regular expression and get the first match. |
replace_blanks(list, placeholder) | Replace null dictionary keys with "" (default) or the specified placeholder value. Example: Attempting to use Jinja2’s sort filter with a list of dictionaries with null values will cause an error. This statement loops over all entries in an activity log while also replacing blank values and then sorting: {% for entry in log | replace_blanks | sort(attribute="tool") %} |
{{p VARIABLE }}
. That variable is automatically replaced with the contents of the subdocument.
jinja2.ext.debug
extension to make it easier for you to debug a template. Place a {% debug %}
tag somewhere in your template.
The next time you generate a report with that template, Ghostwriter will replace the tag with the template’s available context (the report and project data) and filters.
Also, see Troubleshooting Word Templates for a more in-depth explanation of how to troubleshoot a template that gives you problems.