ssp_io
trestle.core.ssp_io
¤
Handle direct IO for writing SSP responses as markdown.
logger
¤
Classes¤
SSPMarkdownWriter
¤
Class to write control responses as markdown.
Source code in trestle/core/ssp_io.py
class SSPMarkdownWriter():
"""Class to write control responses as markdown."""
def __init__(self, trestle_root: pathlib.Path) -> None:
"""Initialize the class."""
self._trestle_root = trestle_root
self._ssp: ssp.SystemSecurityPlan = None
self._resolved_catalog: Catalog = None
self._catalog_interface: CatalogInterface = None
def set_ssp(self, ssp: ssp.SystemSecurityPlan) -> None:
"""Set ssp."""
self._ssp = ssp
def set_catalog(self, resolved_catalog: Catalog) -> None:
"""Set catalog."""
self._resolved_catalog = resolved_catalog
self._catalog_interface = catalog_interface.CatalogInterface(self._resolved_catalog)
def get_control_statement(self, control_id: str, level: int) -> str:
"""
Get the control statement for an ssp - to be printed in markdown as a structured list.
Args:
control_id: The control_id to use.
Returns:
A markdown blob as a string.
"""
if not self._resolved_catalog:
raise TrestleError('Cannot get control statement, set resolved catalog first.')
writer = DocsControlWriter()
control = self._catalog_interface.get_control(control_id)
if not control:
return ''
control_lines = writer.get_control_statement_ssp(control)
return self._build_tree_and_adjust(control_lines, level)
def get_control_part(self, control_id: str, part_name: str, level: int) -> str:
"""Get control part with given name."""
control_part = self._catalog_interface.get_control_part_prose(control_id, part_name)
md_list = self._write_str_with_header(
f'Control Part: {part_name} for control: {control_id}', control_part, level
)
return self._build_tree_and_adjust(md_list.split('\n'), level)
def get_fedramp_control_tables(self, control_id: str, level: int, label_column=False) -> str:
"""Get the fedramp metadata as markdown tables, with optional third label column for params.
The fedramp metadata has the following elements:
- Responsible roles field
- Parameter values table
- Implementation status field
- Control origination field
Returns:
tables as one coherent markdown blob.
"""
resp_roles_table = self.get_responsible_roles_table(control_id, level)
params_values = self._parameter_table(control_id, level, label_column)
impl_status = self.get_fedramp_implementation_status(control_id, level)
control_orig = self.get_fedramp_control_origination(control_id, level)
final_output = ''
if resp_roles_table:
final_output += resp_roles_table
if params_values:
final_output += '\n' + params_values
if impl_status:
final_output += '\n' + impl_status
if control_orig:
final_output += '\n' + control_orig
return final_output
def get_responsible_roles_table(self, control_id: str, level: int) -> str:
"""
For each role id - if the role exists in metadata use the title as what gets printed in the roles table.
If not (for now) warn and use the role-id as the printed text.
"""
if self._ssp is None:
raise TrestleError('Cannot get responsible roles, SSP is not set.')
for impl_requirement in self._ssp.control_implementation.implemented_requirements:
if impl_requirement.control_id == control_id:
if impl_requirement.responsible_roles:
resp_roles = as_list(impl_requirement.responsible_roles)
role_ids = [role.role_id.replace('_', ' ') for role in resp_roles]
# now check if this role exists in the metadata
role_titles = dict(zip(role_ids, role_ids))
roles = as_list(self._ssp.metadata.roles)
for role in roles:
if role.id in role_ids:
role_titles[role.id] = role.title
# dictionary to md table
md_list = self._write_table_with_header(
'Responsible Roles.', [[key, role_titles[key]] for key in role_titles.keys()],
['Role ID', 'Title'],
level
)
return md_list
else:
logger.warning(
f'No responsible roles were found for the control with id: {control_id} in given SSP.'
)
return ''
return ''
def _parameter_table(self, control_id: str, level: int, label_column=False) -> str:
"""Print Param_id | ValueOrLabelOrChoices | Optional Label Column."""
if not self._ssp:
raise TrestleError('Cannot get parameter table, set SSP first.')
writer = DocsControlWriter()
control = self._catalog_interface.get_control(control_id)
if not control:
return ''
params_lines = writer.get_param_table(control, label_column)
# need to make sure no params still have moustaches. convert to brackets to avoid jinja complaints
clean_lines = []
for line in params_lines:
clean_lines.append(line.replace('{{', '[[').replace('}}', ']]'))
tree = MarkdownNode.build_tree_from_markdown(clean_lines)
tree.change_header_level_by(level)
return tree.content.raw_text
def get_fedramp_implementation_status(self, control_id: str, level: int) -> str:
"""
Print implementation status as a list of items, only showing those that are applicable for the control.
This is unlike the word document FedRAMP which uses checkboxes on standard set of options.
Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
"""
if not self._ssp:
raise TrestleError('Cannot get Fedramp implementation status, set SSP first.')
implementation_statuses: List[str] = []
control_impl_req = self._control_implemented_req(control_id)
if control_impl_req and control_impl_req.props:
for prop in control_impl_req.props:
if prop.name == IMPLEMENTATION_STATUS:
implementation_statuses.append(prop.value)
md_list = self._write_list_with_header('FedRamp Implementation Status.', implementation_statuses, level)
return md_list
def get_fedramp_control_origination(self, control_id: str, level: int) -> str:
"""
Print control origination, as a list of items, only showing those that are applicable for the control.
Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
"""
if not self._ssp:
raise TrestleError('Cannot get FedRamp control origination, set SSP first.')
control_origination = []
control_impl_req = self._control_implemented_req(control_id)
if control_impl_req and control_impl_req.props:
for prop in control_impl_req.props:
if prop.name == CONTROL_ORIGINATION:
control_origination.append(prop.value)
md_list = self._write_list_with_header('FedRamp Control Origination.', control_origination, level)
return md_list
def get_control_response(self, control_id: str, level: int, write_empty_responses=False, show_comp=True) -> str:
"""
Get the full control implemented requirements, broken down based on the available control responses.
Args:
control_id: id of the control
level: level of indentation
write_empty_responses: write response even if empty
show_comp: show the component name in the response
Notes:
For components the following structure is assumed:
'This System' is the default response, and other components are treated as sub-headings per response item.
"""
if not self._resolved_catalog:
raise TrestleError('Cannot get control response, set resolved catalog first.')
control = self._catalog_interface.get_control(control_id)
control_impl_req = self._control_implemented_req(control_id)
if not control_impl_req:
logger.info(f'No implemented requirements found for the control {control_id}')
return ''
md_writer = MDWriter(None)
if control_impl_req.statements:
for statement in control_impl_req.statements:
statement_id = statement.statement_id
label = statement_id
part_name = None
# look up label for this statement
if control.parts:
found_label, part = self._catalog_interface.get_statement_label_if_exists(control_id, statement_id)
if found_label:
label = found_label
part_name = part.name
response_per_component = self._get_responses_by_components(statement, write_empty_responses)
if response_per_component or (not response_per_component and write_empty_responses):
if part_name and part_name == 'item':
# print part header only if subitem
header = f'Implementation for part {label}'
md_writer.new_header(level=1, title=header)
for idx, component_key in enumerate(response_per_component):
if component_key == SSP_MAIN_COMP_NAME and idx == 0:
# special case ignore header but print contents
md_writer.new_paragraph()
elif show_comp:
md_writer.new_header(level=2, title=component_key)
md_writer.set_indent_level(-1)
md_writer.new_line(response_per_component[component_key])
md_writer.set_indent_level(-1)
lines = md_writer.get_lines()
tree = MarkdownNode.build_tree_from_markdown(lines)
tree.change_header_level_by(level)
return tree.content.raw_text
def _get_responses_by_components(self, statement: Statement, write_empty_responses: bool) -> Dict[str, str]:
"""Get response per component, substitute component id with title if possible."""
response_per_component = {}
if statement.by_components:
for component in statement.by_components:
# look up component title
subheader = component.component_uuid
response = ''
if self._ssp.system_implementation.components:
for comp in self._ssp.system_implementation.components:
if comp.uuid == component.component_uuid:
title = comp.title
if title:
subheader = title
if component.description:
response = component.description
if response or (not response and write_empty_responses):
if subheader:
response_per_component[subheader] = response
return response_per_component
def _control_implemented_req(self, control_id: str) -> Optional[ssp.ImplementedRequirement]:
"""Retrieve control implemented requirement by control-id."""
requirements = self._ssp.control_implementation.implemented_requirements
for requirement in requirements:
if requirement.control_id == control_id:
return requirement
logger.debug(f'No implemented requirement found for control {control_id}')
return None
def _write_list_with_header(self, header: str, lines: List[str], level: int) -> str:
if lines:
md_writer = MDWriter(None)
md_writer.new_paragraph()
md_writer.new_header(level=1, title=header)
md_writer.set_indent_level(-1)
md_writer.new_list(lines)
md_writer.set_indent_level(-1)
return self._build_tree_and_adjust(md_writer.get_lines(), level)
return ''
def _write_table_with_header(
self, header: str, values: List[List[str]], table_header: List[str], level: int
) -> str:
if values and values[0]:
md_writer = MDWriter(None)
md_writer.new_paragraph()
md_writer.new_header(level=1, title=header)
md_writer.set_indent_level(-1)
md_writer.new_table(values, table_header)
md_writer.set_indent_level(-1)
return self._build_tree_and_adjust(md_writer.get_lines(), level)
return ''
def _write_str_with_header(self, header: str, text: str, level: int) -> str:
if text:
md_writer = MDWriter(None)
md_writer.new_paragraph()
md_writer.new_header(level=1, title=header)
md_writer.set_indent_level(-1)
md_writer.new_line(text)
md_writer.set_indent_level(-1)
return self._build_tree_and_adjust(md_writer.get_lines(), level)
return ''
def _build_tree_and_adjust(self, lines: List[str], level: int) -> str:
tree = MarkdownNode.build_tree_from_markdown(lines)
tree.change_header_level_by(level)
return tree.content.raw_text
Methods¤
__init__(self, trestle_root)
special
¤
Initialize the class.
Source code in trestle/core/ssp_io.py
def __init__(self, trestle_root: pathlib.Path) -> None:
"""Initialize the class."""
self._trestle_root = trestle_root
self._ssp: ssp.SystemSecurityPlan = None
self._resolved_catalog: Catalog = None
self._catalog_interface: CatalogInterface = None
get_control_part(self, control_id, part_name, level)
¤
Get control part with given name.
Source code in trestle/core/ssp_io.py
def get_control_part(self, control_id: str, part_name: str, level: int) -> str:
"""Get control part with given name."""
control_part = self._catalog_interface.get_control_part_prose(control_id, part_name)
md_list = self._write_str_with_header(
f'Control Part: {part_name} for control: {control_id}', control_part, level
)
return self._build_tree_and_adjust(md_list.split('\n'), level)
get_control_response(self, control_id, level, write_empty_responses=False, show_comp=True)
¤
Get the full control implemented requirements, broken down based on the available control responses.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
control_id |
str |
id of the control |
required |
level |
int |
level of indentation |
required |
write_empty_responses |
write response even if empty |
False |
|
show_comp |
show the component name in the response |
True |
Notes
For components the following structure is assumed: 'This System' is the default response, and other components are treated as sub-headings per response item.
Source code in trestle/core/ssp_io.py
def get_control_response(self, control_id: str, level: int, write_empty_responses=False, show_comp=True) -> str:
"""
Get the full control implemented requirements, broken down based on the available control responses.
Args:
control_id: id of the control
level: level of indentation
write_empty_responses: write response even if empty
show_comp: show the component name in the response
Notes:
For components the following structure is assumed:
'This System' is the default response, and other components are treated as sub-headings per response item.
"""
if not self._resolved_catalog:
raise TrestleError('Cannot get control response, set resolved catalog first.')
control = self._catalog_interface.get_control(control_id)
control_impl_req = self._control_implemented_req(control_id)
if not control_impl_req:
logger.info(f'No implemented requirements found for the control {control_id}')
return ''
md_writer = MDWriter(None)
if control_impl_req.statements:
for statement in control_impl_req.statements:
statement_id = statement.statement_id
label = statement_id
part_name = None
# look up label for this statement
if control.parts:
found_label, part = self._catalog_interface.get_statement_label_if_exists(control_id, statement_id)
if found_label:
label = found_label
part_name = part.name
response_per_component = self._get_responses_by_components(statement, write_empty_responses)
if response_per_component or (not response_per_component and write_empty_responses):
if part_name and part_name == 'item':
# print part header only if subitem
header = f'Implementation for part {label}'
md_writer.new_header(level=1, title=header)
for idx, component_key in enumerate(response_per_component):
if component_key == SSP_MAIN_COMP_NAME and idx == 0:
# special case ignore header but print contents
md_writer.new_paragraph()
elif show_comp:
md_writer.new_header(level=2, title=component_key)
md_writer.set_indent_level(-1)
md_writer.new_line(response_per_component[component_key])
md_writer.set_indent_level(-1)
lines = md_writer.get_lines()
tree = MarkdownNode.build_tree_from_markdown(lines)
tree.change_header_level_by(level)
return tree.content.raw_text
get_control_statement(self, control_id, level)
¤
Get the control statement for an ssp - to be printed in markdown as a structured list.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
control_id |
str |
The control_id to use. |
required |
Returns:
Type | Description |
---|---|
str |
A markdown blob as a string. |
Source code in trestle/core/ssp_io.py
def get_control_statement(self, control_id: str, level: int) -> str:
"""
Get the control statement for an ssp - to be printed in markdown as a structured list.
Args:
control_id: The control_id to use.
Returns:
A markdown blob as a string.
"""
if not self._resolved_catalog:
raise TrestleError('Cannot get control statement, set resolved catalog first.')
writer = DocsControlWriter()
control = self._catalog_interface.get_control(control_id)
if not control:
return ''
control_lines = writer.get_control_statement_ssp(control)
return self._build_tree_and_adjust(control_lines, level)
get_fedramp_control_origination(self, control_id, level)
¤
Print control origination, as a list of items, only showing those that are applicable for the control.
Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
Source code in trestle/core/ssp_io.py
def get_fedramp_control_origination(self, control_id: str, level: int) -> str:
"""
Print control origination, as a list of items, only showing those that are applicable for the control.
Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
"""
if not self._ssp:
raise TrestleError('Cannot get FedRamp control origination, set SSP first.')
control_origination = []
control_impl_req = self._control_implemented_req(control_id)
if control_impl_req and control_impl_req.props:
for prop in control_impl_req.props:
if prop.name == CONTROL_ORIGINATION:
control_origination.append(prop.value)
md_list = self._write_list_with_header('FedRamp Control Origination.', control_origination, level)
return md_list
get_fedramp_control_tables(self, control_id, level, label_column=False)
¤
Get the fedramp metadata as markdown tables, with optional third label column for params.
The fedramp metadata has the following elements: - Responsible roles field - Parameter values table - Implementation status field - Control origination field
Returns:
Type | Description |
---|---|
str |
tables as one coherent markdown blob. |
Source code in trestle/core/ssp_io.py
def get_fedramp_control_tables(self, control_id: str, level: int, label_column=False) -> str:
"""Get the fedramp metadata as markdown tables, with optional third label column for params.
The fedramp metadata has the following elements:
- Responsible roles field
- Parameter values table
- Implementation status field
- Control origination field
Returns:
tables as one coherent markdown blob.
"""
resp_roles_table = self.get_responsible_roles_table(control_id, level)
params_values = self._parameter_table(control_id, level, label_column)
impl_status = self.get_fedramp_implementation_status(control_id, level)
control_orig = self.get_fedramp_control_origination(control_id, level)
final_output = ''
if resp_roles_table:
final_output += resp_roles_table
if params_values:
final_output += '\n' + params_values
if impl_status:
final_output += '\n' + impl_status
if control_orig:
final_output += '\n' + control_orig
return final_output
get_fedramp_implementation_status(self, control_id, level)
¤
Print implementation status as a list of items, only showing those that are applicable for the control.
This is unlike the word document FedRAMP which uses checkboxes on standard set of options. Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
Source code in trestle/core/ssp_io.py
def get_fedramp_implementation_status(self, control_id: str, level: int) -> str:
"""
Print implementation status as a list of items, only showing those that are applicable for the control.
This is unlike the word document FedRAMP which uses checkboxes on standard set of options.
Using a LUT to map between structured data fields, defined by FedRAMP and historical text.
"""
if not self._ssp:
raise TrestleError('Cannot get Fedramp implementation status, set SSP first.')
implementation_statuses: List[str] = []
control_impl_req = self._control_implemented_req(control_id)
if control_impl_req and control_impl_req.props:
for prop in control_impl_req.props:
if prop.name == IMPLEMENTATION_STATUS:
implementation_statuses.append(prop.value)
md_list = self._write_list_with_header('FedRamp Implementation Status.', implementation_statuses, level)
return md_list
get_responsible_roles_table(self, control_id, level)
¤
For each role id - if the role exists in metadata use the title as what gets printed in the roles table.
If not (for now) warn and use the role-id as the printed text.
Source code in trestle/core/ssp_io.py
def get_responsible_roles_table(self, control_id: str, level: int) -> str:
"""
For each role id - if the role exists in metadata use the title as what gets printed in the roles table.
If not (for now) warn and use the role-id as the printed text.
"""
if self._ssp is None:
raise TrestleError('Cannot get responsible roles, SSP is not set.')
for impl_requirement in self._ssp.control_implementation.implemented_requirements:
if impl_requirement.control_id == control_id:
if impl_requirement.responsible_roles:
resp_roles = as_list(impl_requirement.responsible_roles)
role_ids = [role.role_id.replace('_', ' ') for role in resp_roles]
# now check if this role exists in the metadata
role_titles = dict(zip(role_ids, role_ids))
roles = as_list(self._ssp.metadata.roles)
for role in roles:
if role.id in role_ids:
role_titles[role.id] = role.title
# dictionary to md table
md_list = self._write_table_with_header(
'Responsible Roles.', [[key, role_titles[key]] for key in role_titles.keys()],
['Role ID', 'Title'],
level
)
return md_list
else:
logger.warning(
f'No responsible roles were found for the control with id: {control_id} in given SSP.'
)
return ''
return ''
set_catalog(self, resolved_catalog)
¤
Set catalog.
Source code in trestle/core/ssp_io.py
def set_catalog(self, resolved_catalog: Catalog) -> None:
"""Set catalog."""
self._resolved_catalog = resolved_catalog
self._catalog_interface = catalog_interface.CatalogInterface(self._resolved_catalog)
set_ssp(self, ssp)
¤
Set ssp.
Source code in trestle/core/ssp_io.py
def set_ssp(self, ssp: ssp.SystemSecurityPlan) -> None:
"""Set ssp."""
self._ssp = ssp
handler: python