Modules, Questions, and Documents YAML Reference¶
A module and its questions are defined in YAML specification files. The schema for the specification files is as follows:
Module¶
Each file is a Module. A Module has the following required fields:
id: module_id
title: Your Title Here
The module_id
must match the file name that the YAML is saved into,
without the path or file extension.
Several optional fields can be specified:
type: project
version: 1
instance-name: "Module for {{q1}}"
invitation-message: "Can you tell me about {{question.text}} and let me know when you are done?"
icon: app.png
The type
field is set to project
just when the Module is to be
offered to users when they start a new Project. (system-project
is
used for project-like modules that are system controlled and not offered
to the user.)
The version
field is used only to force changes in the specification
to be considered incompatible with any existing user answers (see
Updating Modules).
The instance-name
is a template to generate a dynamic title for
in-progress and completed modules. The instance-name
is rendered
like other Module documents but it is always specified in text
format (see Documents).
For modules that define the root of an application, icon
specifies a
static asset (in the assets
directory) to use as an application
icon.
In addition, a Module may have an introduction
document (for
projects, the introduction appears at the top of the project page; for
other module types, it appears as an implicit initial interstitial) and
a list of one or more output
documents. For example:
introduction:
format: markdown
template: |
Welcome to the module.
This module should take you two minutes.
output:
- title: Document 1
format: markdown
template: |
# Document for {{project}}
Hello! This is the output of the module. You answered {{q1}}.
- title: Document 2
glyphicon: dashboard
format: html
template: |
<h1># Document for {{project}}</h1>
<p>Hello! This is the output of the module. You answered {{q1}}.</p>
The format of documents are described in a later section.
Finally, a Module contains a list of one or more Questions:
questions:
- id: q1
title: Your Favorite Animal
prompt: What's your favorite animal?
type: text
- id: q2
title: What Kind of Animal Is It
prompt: How would you classify this animal?
type: choice
choices:
- key: pet
text: common pet
help: Is this animal a common pet?
- key: wild
text: wild animal
help: Is this animal an undomesticated wild animal?
The schema for questions is documented in a later section.
Additional fields for projects¶
Question Fields¶
The questions of projects are displayed in a layout of tabs and groups within each tab pane. Each question that shows up on a project page should specify its tab and group name (which are also the display strings):
questions:
- id: howto_ssp
title: "SSP 101: What's a System Security Plan"
type: module
module-id: howto_ssp
tab: How To
group: Start Here
icon: ssp.png
icon
specifies a static asset (in the assets
directory) to use
as an icon for the question. If the question’s type is module
and it
is answered and the answer is a Task that has a top-level icon
field, then the answer’s icon is used instead.
Instead of tab
and group
, placement: action-buttons
can be
used instead to show the question in an action bar above the tabs,
rather than in tabs.
Output Document Fields¶
Output documents of a project module that have an id
field are used
in the following ways:
They are displayed in the Related Controls page for the project. Add a
title
attribute to set the heading text above the document’s content.They can be accessed from higher-level apps into which this app has been added. In a higher-level app, access the rendered HTML value of the output document as
{{question.output_documents.document_id}}
.
When display: top
is set on an output document, it is rendered above
the Your Answers section.
Test Answers¶
Projects can provide sets of exemplar answers for use in test scripts. e.g.:
tests:
test1: # <-- test suite ID
description: "Sample data."
answers:
q1:
answers: # <-- answers to sub-task's questions
q1: desktop
q2: My Secure Tool
Documents¶
Documents occur as introduction
and output
documents of Modules,
and a restricted form of documents also occurs in Question prompts (see
Questions below). A document appearing in the output documents list is
given as:
output:
- id: mydoc
title: Document 1
format: markdown
template: |
Hello!
The id
and title
fields are generally optional and are used for
output documents only. An id
is required to make the document
downloadable. The fields also have special uses in projects (see above).
The format
field is described below.
The document can also be stored in a separate file by replacing the document data in the module YAML file with a filename and placing the document properties and template in the named file, as in:
# module.yaml
output:
- mydoc.md
# mydoc.md
id: mydoc
title: Document 1
format: markdown
...
Hello!
When using a separate file, the document properties (id
, title
,
and format
) are given in a YAML block at the top of the file. A line
containing just three dots signifies the end of the YAML block,
separating it from the document template. The document template follows.
Document Format¶
The introduction
and output
documents of Modules allow a format
to be specified. The document formats are:
markdown
— The document is entered in CommonMark (quick guide) in the specification file, but it will be rendered into a richly formatted presentation on screen.html
— The document is given in raw HTML, but it will be rendered on screen.text
— The document is given in plain text, and it will display as preformatted (fixed-width) text on screen.json
,yaml
— Experimental.
Additional Markdown Notes¶
Documents specified in markdown
format are rendered according to the
CommonMark 0.25 specification.
Note that for some things like tables, it is necessary to insert raw HTML right into the document, which is acceptable CommonMark. To create a table:
<table><thead><th>
Col 1
</th>
<th>
Col 2
</th>
</thead>
<tbody><tr><td>
Some [commonmark](http://www.google.com) within the cell.
</td>
<td>
More *content.*
</td></tr></tbody></table>
Some of the newlines are necessary to get CommonMark to go out of raw HTML mode and back into parsing CommonMark.
Document Templating¶
All document formats are evaluated as Jinja2 templates. That means within your document you can embed special tags that are replaced prior to the document being displayed to the user:
{{ question_id }}
will be replaced with the user’s answer to the question whoseid
isquestion_id
. For choice-type questions, the value is replaced by the choicekey
. Use{{ question_id.text }}
to get display text. See the question types documentation below for details.{% if question_id == 'value' %}....{% endif %}
is a conditional block. The contents inside the block (....
) will be included in the output if the condition is true. In this example, the contents inside the block will be included in the output if the user’s answer toquestion_id
isvalue
.
Output documents and question prompts have access to the user’s answers to questions in question variables. (The introduction document does not have access to the user’s answers because questions have not yet been answered.)
The following information is also available within the output template
for each question as of version v0.8.6
:
{{ question_id.not_yet_answered }}
Question has not yet been answered.{{ question_id.answered }}
Question has an answer either by user or was imputed, but not imputednull
or answerednull
.{{ question_id.imputed }}
Question considered “answered” but no TaskAnswerHistory record exists in the database for question meaning a user didn’t provide the answer.{{ question_id.skipped }}
Question has a null answer either because imputednull
or the user skipped it.{{ question_id.skipped_by_user }}
Question has anull
answer because used a skip button (e.g., question wasn’t imputednull
).{{ question_id.skipped_reason }}
Question’s indicated reason for skipping (e.g. “I don’t know” or “It doesn’t apply”){{ question_id.unsure }}
If question was answered by a user, its unsure flag. (NOTE: Purpose of this flag was to allow users to indicate uncertainty in the answer. Due to usability issues however, this feature is currently hidden.){{ question_id.date_answered }}
Question answered date.{{ question_id.reviewed_state }}
Question reviewed value.
All documents also have access to the project title as {{project}}
.
Project Documents¶
In addition to the output
documents described above, a project
module may also have a snippet
that defines how a project appears in
the project listing page:
snippet:
format: markdown
template: |
Project {{name}}
Module Assets¶
Modules often make use of assets outside of the YAML file.
Static Assets¶
Static assets such as images can be referenced in module content
(introductions, question prompts, and output documents). These assets
are exposed by the Q web server in its static path. Place static assets
in an assets
subdirectory where the module is. When the asset is
referenced in a Markdown document template, its path will be rewritten
to be its public (virtual) path on the web server.
For example, to include an image in a module introduction add the image in the Markdown template:
module.yaml
-----------
...
format: markdown
template: |
![](my_image.png)
...
Place the module and image files at the path:
module.yaml
assets/my_image.png
Private Assets¶
Private assets are other files that are stored with a module but are not exposed by the web server. The directory provides a place to store files for internal use during module development.
Place private assets in a private-assets
subdirectory next to the
module YAML file.
Questions¶
Questions have the following required fields:
- id: q1
title: Your Favorite Animal
prompt: What's your favorite animal?
type: text
The question id
is used to refer to this Question in other questions
and in the output documents.
The title
is used to describe the Question in places where a
long-form prompt would not be appropriate.
The prompt
is the text the user is prompted with when presented with
the question. The prompt is rendered like other Module documents but it
is always specified in markdown
format (see Documents). The first
line (paragraph) of the prompt is shown in larger, bold type.
A question may have other optional fields that provide the user with other information, such as:
examples:
- example: |
First example.
- example: |
Second example.
reference_text: See NIST SP 800-171 page 102.
Like the prompt
, each entry inside examples
and the
reference_text
are Markdown templates.
Removing a question, changing a question type, and other changes as noted below are incompatible changes (see Updating Modules).
Question Types¶
text
¶
This type asks the user for a single line of free-form text. The text cannot be empty.
A placeholder
can be specified which places ghosted “placeholder”
text inside the form field when the user has not yet entered anything. A
default
value can be specified, instead, which fills in the field
with a value that the user can edit (or not) before submitting the
answer. The placeholder and default fields are rendered like other
Module documents — just like the prompt
.
help
text can be specified which provides an additional prompt
smaller and below the field input.
Example:
- id: q1
title: Your Favorite Animal
prompt: What's your favorite animal?
type: text
placeholder: enter a type of animal
help: Examples: dog, cat, turtle, lion
In document templates and impute conditions, the value of text
questions is simply the text the user entered.
password
¶
This type asks the user for a password. It is the same as the text
question type, except that a password input field is used to mask the
input. help
can be specified. placeholder
and default
are
not allowed.
email-address
¶
This type asks the user for an email address. It is the same as the
text
question type, except that the value entered must be a valid
email address. placeholder
, default
, and help
can be
specified.
url
¶
This type asks the user for a web address (a URL). It is the same as the
text
question type, except that the value entered must be a valid
web address. placeholder
, default
, and help
can be
specified. The web address is not checked for existence — only the form
(syntax) of the address is checked.
longtext
¶
This type asks the user for free-form text using a large rich text input area that allows for multiple lines of text and some simple formatting. The text cannot be empty.
A default
value can be specified, which fills in the field with a
value that the user can edit (or not) before submitting the answer. The
field is rendered like other Module documents — just like the
prompt
. It is given in Markdown.
help
text can be specified which provides an additional prompt
smaller and below the field input.
In document templates and impute conditions, the value of longtext
questions is the text the user entered, as a string, with rich formatted
represented in CommonMark. In document templates, the text is
automatically converted back to rich formatting.
date
¶
This type asks the user for a date.
help
text can be specified which provides an additional prompt
smaller and below the field input.
In document templates and impute conditions, the value of date
questions is a text string in YYYY-MM-DD format.
choice
¶
This type asks the user to choose one of several options. The options are given as:
choices:
- key: pet
text: common pet
help: Is this animal a common pet?
- key: wild
text: wild animal
help: Is this animal an undomesticated wild animal?
The user must select exactly one choice.
The help
text is optional. It is displayed smaller and below each
choice. (Unlike some other question types, there is no help
field on
the question as a whole.)
In document templates and impute conditions, the value of choice
questions is the key
of the choice selected by the user. Use
questionid.text
to access the display text for the choice.
Removing a choice is an incompatible change (see Updating Modules).
yesno
¶
This type is the same as choice
but with built-in choices for yes
and no. It is the same as a choice
question type with these choices:
choices:
- key: yes
text: Yes
- key: no
text: No
The user must choose either yes or no.
multiple-choice
¶
The multiple-choice
question type is similar to the choice
question type except that:
The user can select multiple choices.
In document templates and impute conditions, the value of
multiple-choice
questions is a list of thekey
s of the choices selected by the user. When used bare, this renders as a comma-separated list of keys. One can use the`|length
filter <http://jinja.pocoo.org/docs/dev/templates/#length>`__ and{% for ... in ... %}... {% endfor %}
loops to access the individual choices the user selected. Usequestionid.text
to render a comma-separated list of the display text of the selected choices.min
andmax
may be specified. Ifmin
is specified, it must be greater than or equal to zero and requires that the user choose at least that many choices. Ifmax
is specified, it must be greater than or equal to one (and ifmin
is specified, it must be at leastmin
) and requires that the user choose at most that number of choices.
Increasing the min
or decreasing the max
are incompatible
changes (see Updating Modules).
datagrid
¶
The datagrid
question type is similar to the multiple-choice
question type except that:
An array of fields define columns for tabular data (AKA “datagrid”).
Users can enter as many rows of data as desired.
Results are usually displayed as a table.
min
andmax
may be specified. Ifmin
is specified, it must be greater than or equal to zero and requires that the user add at least that many rows. Ifmax
is specified, it must be greater than or equal to one (and ifmin
is specified, it must be at leastmin
) and prevents the user from adding more than that number rows.render
key can be added and set tovertical
to force the tabular data to render in a vertical format with the fields listed vertically on the side instead of horizontally. This is good for rendering information about a single, multi-field entry such as an address or Point of Contact.
Increasing the min
or decreasing the max
are incompatible
changes (see Updating Modules).
integer
¶
This question type asks for a numeric, integer input.
If min
and max
are set, then the value is restricted to that
range. If min
is omitted, then negative numbers are allowed!
As with the text question types, placeholder
and help
text can
also be specified.
In document templates and impute conditions, the value of integer
questions is the numeric value entered by the user.
real
¶
This question type asks for a numeric input, allowing for real (floating-point) numbers.
If min
and max
are set, then the value is restricted to that
range. If min
is omitted, then negative numbers are allowed!
As with the text question types, placeholder
and help
text can
also be specified and in document templates and impute conditions the
value of these questions is the numeric value entered by the user. .
file
¶
This question type asks the user to upload a file.
help
text can also be specified, as in the text question types.
By default, any type of file is permitted to be uploaded. If the
optional file-type
field is set, the uploaded file is validated to
be of a particular type. Supported values for the file-type
field
are:
image
: Ensures the file is an image. The uploaded file is converted to PNG format internally.
If file-type
is image
, then some image transformation can be
run, e.g.:
- id: logo
title: Logo
prompt: Upload a logo.
type: file
file-type: image
image:
max-size:
width: 60
height: 60
If image->max-size
is given, then the image will be resized prior to
being saved internally so that its width and height do not exceed the
given dimensions.
In document templates and impute conditions, the value of these
questions is a Python dict (JSON object) containing url
(a download
URL) and size
(in bytes) fields.
module
, module-set
¶
These question type prompt the user to select another completed module
as the answer to the question. The module-id
field specifies the ID
of another module specification. The module
question type allows for
a single other module to answer the question. The module-set
question type allows for zero or more other modules to answer the
question.
The module-id
field specifies a module ID as it occurs in the id
field of another YAML file in the same application.
Example¶
Here’s an example of the module
question type:
- id: evidence
title: Evidence
type: module
module-id: evidence
prompt: |
Provide evidence of your properly configured firewall, if possible.
impute:
- condition: not(have_other_dmz == 'ad_hoc_dmz')
value: ~
App protocols¶
Instead of using module-id
, a protocol
can be specified instead.
A protocol is a globally unique identifier that apps in the Compliance
Store use to indicate that their questions and output documents meet a
certain criteria (i.e. implement the protocol). When a user attempts to
answer a module
or module-set
question that uses protocol
instead of module-id
, instead of starting a particular named module,
the user instead can start any app from the Compliance Store that
implements the protocol.
For example:
- id: evidence
title: Evidence
type: module
protocol: govready.com/apps/compliance/2017/nist-sp-800-171-r1-ssp
prompt: |
Provide evidence of your properly configured firewall, if possible.
When a user answers this question, they will be redirected to the
Compliance Store but will be offered only apps that implement the
protocol govready.com/apps/compliance/2017/nist-sp-800-171-r1-ssp
.
An app implements a protocol by having a protocol:
field at the top
level of the app’s YAML specification file with the same value. For
instance, the following app would be offered in the Compliance Store for
this example question:
id: app
title: My App
type: project
protocol: govready.com/apps/compliance/2017/nist-sp-800-171-r1-ssp
Both protocol fields can be either a single string or a list of strings.
When the question protocol
value is a list, then only apps which
implement all of the listed protocols will be offered.
Question type details¶
Changing the module-id
or protocol
is considered an incompatible
change (see Updating Modules), and if the referenced Module’s
specification is changed on disk in an incompatible way with existing
user answers, the Module in which the question occurs is also considered
to have an incompatible change. Thus an incompatible change in a module
triggers an incompatible change in any other Module that refers to it
(and so on recursively).
In document templates and impute conditions, the value of module
questions is a dictionary of the answers to that module. For example, if
q5
is the ID of a question whose type is module
, then
{{q5.q1}}
will provide the answer to q1
within the module the
user selected that answers q5
.
interstitial
¶
An interstitial
question is not really a question at all! The
prompt
contains template content, as with other questions, but it is
typically longer content with deeper explanatory text. The user is not
asked to enter any information.
In document templates and impute conditions, the value of
interstitial
questions is always a null value.
raw
¶
This type is meant for questions that are always imputed (i.e. that are never presented to the user) and where the answer value can be any JSON-serializable Python data structure, as given by the impute value (see Imputing Answers below).
This question type should be avoided if one of the other question types
specifies a more narrow data type. For instance, if the imputed value is
always a string, the text
or longtext
question types should be
used instead.
Imputing Answers¶
The answer to one question may provide the answer to another. In such cases, the latter question is said to have an imputed value and the user is not asked to answer the question. To impute a value, specify on the question whose value is being imputed:
impute:
- condition: q1 == 'no'
value: don't know
This example says that if the answer to q1
is no
, then the
answer to this question is don't know
.
The condition
is a Jinja2
expression.
Any question can be referred to in the expression (by its id
).
Questions are tested on their internal values. For choice
and
multiple-choice
questions, their values are their key
s, not
their label text, and multiple-choice
questions are lists of keys.
If condition
is omitted, the imputed value is always taken (i.e. the
condition is implicitly met).
The value
provided must be a valid value for the question type it is
a part of. For choice
questions, the value must be a choice key
,
not the label text. For multiple-choice
questions, the value must be
a list of keys.
Multiple condition/value blocks can be provided. They are evaluated in order, with the first matching condition taking precedence.
impute:
- condition: q1 == 'no'
value: I don't know.
- condition: q1 == 'yes'
value: I do know.
The value
field can be evaluated as a Jinja2
expression,
just like the condition, if value-mode
is set to expression
.
This can be used to pull forward the answers of previous questions:
impute:
- condition: q1 == 'same-as-q0'
value: q0
value-mode: expression
value-mode
can also be template
to evaluate the value as a
Jinja2 template, which will yield a text value.
In both conditions and expression
-type values, as well as in
documents, the variables you can use are:
id
s of questions in the modulequestion_id.subquestion_id
to access questions within the tasks that are assigned as answers tomodule
-type questionsproject
, which gives the project nameproject.question_id
,project.question_id.subquestion_id
, etc. to access questions within the projectorganization
, which gives the organization name
We also have a function to retrieve the URL of a module’s static assets, e.g.:
<script src="{{static_asset_path_for('myscript.js')}}"></script>
Question Order¶
The order in which Questions are asked is determined through an algorithm. The algorithm determines which questions need to be asked before other questions and which need to be asked in order to generate the output documents.
The only Questions that are asked of the user are those that are mentioned in any of the output templates or other Questions that required to be asked before those mentioned Questions can be answered.
If a Question mentions another question in its prompt text or impute conditions, the other question must be answered first. A Question can also list other Questions that should be answered first as:
ask-first:
- q1
- q2
Updating Modules¶
When a Module file specification is changed, the change is considered “compatible” or “incompatible” with existing user answers.
Many changes are “compatible”: Changing the introduction or output documents, question prompts, and adding new questions and choices are all compatible changes. These changes can be made “live” on any existing user answers.
Other changes are “incompatible”: Removing a choice is an incompatible change because a user may have already chosen it. Removing a question is incompatible because it would result in a loss of user data.
When there is an incompatible change in a Module specification, a new iteration of the Module will be stored in the program database but existing user answers will continue to be tied to the previous iteration of the Module specification.