Trestle author jinja - output templating support for oscal documents.¤
Unfortunately OSCAL documents are not yet universally accepted. Therefore to support various OSCAL and non-OSCAL compliance workflows trestle author jinja
is designed to provide end users with the ability to use jinja to produce customized output. This complements the more structured commands trestle author catalog-{assemble|generate}
, trestle author profile-{assemble|generate}
and trestle author ssp-{assemble|generate}
and allows arbitrary use of jinja.
Jinja and jinja extensions provided by trestle.¤
Jinja is a powerful templating engine that is both more flexible that pure 'Moustache' approaches, and not coupled to a particular web application server (as an example Django templates). Users are encouraged to review the template designer documentation for jinja as all core functionality is exposed.
Trestle's implementation of the Jinja command works in the following way:
- The template search space, by default, is relative to the current working directory.
- Trestle can inject a lookup table, into the jinja variables to contain booleans / substitutions required by end users using Moustache style variable substitutions.
- Trestle can provide a number of interfaces to OSCAL objects, currently a resolved catalog and a SSP, into jinja.
- Trestle supports custom jinja tags for importing.
- The jinja templating is recursive, to ensure all jinja tags are resolved as appropriate.
More details will be on each of these points below.
CLI invocation¤
Note the examples here use markdown, however, jinja can quite easily target xml or html if used w/o specific markdown content.
trestle author jinja -i input_template.md.jinja -o output_file.md -ssp SSP_NAME -p PROFILE_NAME -lut lookup_table.yaml -elp lut.prefix
-i
input file path, relative to cwd. Users are encouraged to use thefile_name.target_extension.jinja
best practice as it helps mitigate issues, however is not required.-o
final output path, relative to cwd.-ssp
(optional) ssp name (in the trestle project). When used the jinja template will have assp_md_writer
variable exposed to use.-p
(optional) profile name (in the trestle project). When used the jinja template will haveresolved_catalog
andcatalog_interface
variables to use.-lut
(optional) loads yaml into a dictionary in python for which each (top level) variable is available in jinja.-elp
(optional) a period separated prefix for the variables in the lookup table. E.g. if the lut containedbanana: yellow
and the prefix wasfruit.tropical
using{{ fruit.tropical.banana }}
would print outyellow
in the jinja template.-bf
(optional) use to provide a custom formatting of the substituted parameters in the text with brackets or markup formatting. Use dot (.) to indicate where the parameter value will be written. E.g.-bf *.*
to italicize all substituted parameters,-bf Prefix:.
to addPrefix:
to all parameters, and-bf [.]
to put square brackets around the parameters.-vap
(optional) use to specify a--value-assigned-prefix
in front of parameters that have values assigned by the profile. An example would include the organization doing the assignment, e.g.-vap "ACME Assignment:"
This is identical to the behavior ofprofile-resolve
.-vnap
(optional) use to specify a--value-not-assigned-prefix
in front of parameters that do not have values assigned by the profile. An example would be-vap "Assignment:"
This is identical to the behavior ofprofile-resolve
.
Sample jinja templates¤
Below is a sample jinja template for SSP.
template | Description | Optional args required |
---|---|---|
Simple SSP template | Sample ssp jinja template which prints out all control responses and includes a front-matter section | -ssp and -p for the ssp json and corresponding profile, respectively. Also requires a frontmatter.md file |
Variable availability in the jinja template.¤
LUT¤
The lookup table is primarily used for string substitution and to provide variables for basic logic operations in jinja e.g.
The LUT:
names:
OSCAL: Open Security Compliance Assessment Language
trestle_pip: compliance-trestle
trestle_module: trestle
variables:
mac_os: true
The Jinja template:
Install via pip install {{ names.trestle_pip }} and invoke at the python REPL by import {{ names.trestle_module }}
{% if variables.mac_os %}
Users are recommended to use homebrew to install the latest python 3 and then install python within a venv.
{% endif %}
The output:
Install via pip install compliance-trestle and invoke at the python REPL by import trestle
Users are recommended to use homebrew to install the latest python 3 and then install python within a venv.
Users are free to use the LUT to inject more complex variables (arrays of data etc) to use at their own will using standard jinja templating.
Resolved catalog interface (profile)¤
Passing -p
exposes a catalog, resolved from the profile, at catalog
and a trestle.core.catalog_interface.CatalogInterface
at catalog_interface
.
This allows user to perform various task such as iterating ove reach group and printing the group title.
{% for group in catalog_interface.get_all_groups_from_catalog() +%}
## {{ group.title }} {{ group.class }} \({{ group.id|upper }}\)
SSP interface.¤
If -ssp
is passed a variable within the jinja template called ssp_md_writer
is made available which is an instance of trestle.core.ssp_io.SSPMarkdownWriter
.
-ssp
requires that -p
has also been set.
This as allows users, as an example to print out a control response, as markdown
#### What is the solution and how is it implemented?
{{ ssp_md_writer.get_control_response('my_control_id', 3)}}
Custom Jinja tags.¤
Trestle provides custom jinja tags for use specifically with markdown: mdsection_include
and md_clean_include
.
md_clean_include
is similar to the native {% include 'sub_template' %}
that jinja provides except for the following:
md_clean_include
will look for yaml headers in the markdown content and exclude it from the templatemd_clean_include
can be used with an optional keyword argumentheading_level
argument{% md_clean_include 'path_to_file.md' heading_level=2 %}
- The heading level argument adjusts to (based on the number of hashes) the most significant heading in the document, if headings exist.
mdsection_include
is similar to md_clean_include
except that:
-
mdsection_include
requires an second positional argument which is the title of a heading, from a markdown file, which you want the content from. -
E.g:
{% mdsection_include 'test_markdown.md' '# Header we want' %}
-
mdsection_include
can be used with an optional keyword argumentheading_level
argument similar tomd_clean_include
-
{% mdsection_include 'test_markdown.md' '# Header we want' %}
- The heading level argument adjusts to (based on the number of hashes) the most significant heading in the chosen section, if headings exist.
md_datestamp
inserts a date stamp into the output. By default the date is in the format '%Y-%m-%d', e.g. '2021-12-28' and is followed by a double newline to prevent subsequent headings failing to parse correctly. E.g: {% md_datestamp %}
results in a date in the format '2021-12-28' being inserted. md_datestamp
can be used with the following optional keyword arguments:
format
where a python datetime strftime format string is provided to format the output. E.g.{% md_datestamp format='%B %d, %Y' %}
results inDecember 28, 2021
being inserted.newline
is a boolean to control the addition of a double newline after the inserted date string. For example{% md_datestamp newline=false %}
inserts a date in the default format, without additional newlines.
Custom Jinja filters.¤
Trestle provides custom jinja filters for help processing SSP data.
as_list
will return the passed value, or an empty list ifNone
is passed in.- Example:
{% for party in ssp.metadata.parties | as_list %}
get_default
operates the same as the built-in Jinjadefault
filter, with the optional second parameter set toTrue
- Example:
{{ control_interface.get_prop(user, 'user-property') | get_default('[Property]') }}
first_or_none
will return the first element of a list, orNone
if the list is empty orNone
itself.- Example:
{% set address = party.addresses | first_or_none %}
get_party
will return theParty
found inssp.metadata.parties
for a givenuuid
- Example:
{% set organization = party.member_of_organizations | first_or_none | get_party(ssp) %}
parties_for_role
will yield individualParty
entries when given a list ofResponsibleParty
and arole-id
- Example:
{% for party in ssp.metadata.responsible_parties | parties_for_role("prepared-by", ssp) %}
diagram_href
will return theLink.href
whereLink.rel == 'diagram'
when given aDiagram
object- Example:
![{{diagram.caption}}]({{ diagram | diagram_href }})
Generate controls as individual markdown pages.¤
Trestle's Jinja functionality allows its users to generate individual markdown pages for each control from a resolved profile catalog. Such functionality can be used later on to pack individual pages into docs of various formats.
When --docs-profile
or -dp
flag is provided as part of the trestle author jinja
command, the provided Jinja template will be used to generate a markdown page for each control in each group.
For example, suppose we would like to generate the markdown page for each control that would contain Control Objective
, Control Statement
, Expected Evidence
, Implementation Guidance
and say Table of Parameters
used for this control.
To achieve that, we can create a simple Jinja template that would be used to generate each page:
# Control Page
{{ control_writer.write_control_with_sections(
control,
profile,
group_title,
['statement', 'objective', 'expected_evidence', 'implementation_guidance', 'table_of_parameters'],
{
'statement':'Control Statement',
'objective':'Control Objective',
'expected_evidence':'Expected Evidence',
'implementation_guidance':'Implementation Guidance',
'table_of_parameters':'Control Parameters'
}
)
| safe
}}
The template above, would call a control writer that would print the required sections (specified in the list) with the provided headers (specified in the dictionary).
We can then generate individual markdown pages by executing:
trestle author jinja -i profile_to_docs.md.jinja -o controls -p some_profile -dp
This will create a folder named controls
, that would contain a folder per each group and a markdown file per each control in that group. Each markdown file would be formatted using the Jinja template above.
The generated markdown files can then be assembled to the docs of the desired format by adding an indexing page.