Skip to content

trestle.core.ssp_io

trestle.core.ssp_io ¤

Handle direct IO for writing SSP responses as markdown.

Attributes¤

logger = logging.getLogger(__name__) module-attribute ¤

Classes¤

SSPMarkdownWriter ¤

Class to write control responses as markdown.

Functions in this class are mainly used by jinja and not by the trestle code itself.

Source code in trestle/core/ssp_io.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
class SSPMarkdownWriter():
    """
    Class to write control responses as markdown.

    Functions in this class are mainly used by jinja and not by the trestle code itself.
    """

    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: bool = 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: bool = 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 = DocsMarkdownNode.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

    @staticmethod
    def _write_component_prompt(
        md_writer: MDWriter,
        comp_name: str,
        prose: str,
        rules: List[str],
        status: str,
        show_rules: bool,
        show_status: bool
    ) -> None:
        header = f'Component: {comp_name}'
        md_writer.new_header(1, header)
        md_writer.set_indent_level(-1)
        md_writer.new_line(prose)
        md_writer.set_indent_level(-1)
        if rules and show_rules:
            md_writer.new_header(2, title='Rules:')
            md_writer.set_indent_level(-1)
            md_writer.new_list(rules)
            md_writer.set_indent_level(-1)
        if status and show_status:
            md_writer.new_header(2, title=f'Implementation Status: {status}')

    def get_control_response(
        self,
        control_id: str,
        level: int,
        write_empty_responses: bool = False,
        show_comp: bool = True,
        show_rules: bool = False,
        show_status: bool = 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:
            This is intended to be invoked from a jinja template that has already written out the prompt for
            control response
        """
        if not self._resolved_catalog:
            raise TrestleError('Cannot get control response, set resolved catalog first.')

        control = self._catalog_interface.get_control(control_id)
        imp_req = self._control_implemented_req(control_id)
        if not imp_req:
            logger.info(f'No implemented requirements found for the control {control_id}')
            return ''

        md_writer = MDWriter(None)

        system_prose = ''
        system_rules = []
        system_status = STATUS_OPERATIONAL
        imp_req_responses = self._get_responses_by_components(imp_req, write_empty_responses)
        if SSP_MAIN_COMP_NAME in imp_req_responses:
            system_prose, system_rules, system_status = imp_req_responses[SSP_MAIN_COMP_NAME]

        SSPMarkdownWriter._write_component_prompt(
            md_writer, SSP_MAIN_COMP_NAME, system_prose, system_rules, system_status, show_rules, show_status
        )

        # if a control has no statement sub-parts then get the response bycomps from the imp_req itself
        # otherwise get them from the statements in the imp_req
        # an imp_req and a statement are both things that can have bycomps
        has_bycomps = imp_req.statements if imp_req.statements else [imp_req]
        for has_bycomp in has_bycomps:
            statement_id = getattr(has_bycomp, 'statement_id', f'{control_id}_smt')
            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(has_bycomp, write_empty_responses)

            if response_per_component or 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(1, title=header)
                for comp_name, comp_response in response_per_component.items():
                    if comp_name == SSP_MAIN_COMP_NAME:
                        continue
                    prose, rules, status = comp_response
                    if show_comp:
                        SSPMarkdownWriter._write_component_prompt(
                            md_writer, comp_name, prose, rules, status, show_rules, show_status
                        )

        lines = md_writer.get_lines()

        tree = DocsMarkdownNode.build_tree_from_markdown(lines)
        tree.change_header_level_by(level)

        return tree.content.raw_text

    def _get_responses_by_components(self, has_bycomps: TypeWithByComps,
                                     write_empty_responses: bool) -> Dict[str, Tuple[str, List[str], str]]:
        """Get response per component, substitute component id with title if possible."""
        response_per_component: Dict[str, Tuple[str, str]] = {}
        for by_comp in as_list(has_bycomps.by_components):  # type: ignore
            # look up component title
            subheader = by_comp.component_uuid
            prose = ''
            status = ''
            rules = []
            if self._ssp.system_implementation.components:
                for comp in self._ssp.system_implementation.components:
                    if comp.uuid == by_comp.component_uuid:
                        title = comp.title
                        if title:
                            subheader = title
            if by_comp.description:
                prose = by_comp.description
            if by_comp.implementation_status:
                status = by_comp.implementation_status.state
            rules, _ = ControlInterface.get_rule_list_for_item(by_comp)

            if prose or (not prose and write_empty_responses):
                if subheader:
                    response_per_component[subheader] = (prose, rules, status)

        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 = DocsMarkdownNode.build_tree_from_markdown(lines)
        tree.change_header_level_by(level)

        return tree.content.raw_text
Functions¤
__init__(trestle_root) ¤

Initialize the class.

Source code in trestle/core/ssp_io.py
43
44
45
46
47
48
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(control_id, part_name, level) ¤

Get control part with given name.

Source code in trestle/core/ssp_io.py
81
82
83
84
85
86
87
88
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(control_id, level, write_empty_responses=False, show_comp=True, show_rules=False, show_status=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 bool

write response even if empty

False
show_comp bool

show the component name in the response

True
Notes

This is intended to be invoked from a jinja template that has already written out the prompt for control response

Source code in trestle/core/ssp_io.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def get_control_response(
    self,
    control_id: str,
    level: int,
    write_empty_responses: bool = False,
    show_comp: bool = True,
    show_rules: bool = False,
    show_status: bool = 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:
        This is intended to be invoked from a jinja template that has already written out the prompt for
        control response
    """
    if not self._resolved_catalog:
        raise TrestleError('Cannot get control response, set resolved catalog first.')

    control = self._catalog_interface.get_control(control_id)
    imp_req = self._control_implemented_req(control_id)
    if not imp_req:
        logger.info(f'No implemented requirements found for the control {control_id}')
        return ''

    md_writer = MDWriter(None)

    system_prose = ''
    system_rules = []
    system_status = STATUS_OPERATIONAL
    imp_req_responses = self._get_responses_by_components(imp_req, write_empty_responses)
    if SSP_MAIN_COMP_NAME in imp_req_responses:
        system_prose, system_rules, system_status = imp_req_responses[SSP_MAIN_COMP_NAME]

    SSPMarkdownWriter._write_component_prompt(
        md_writer, SSP_MAIN_COMP_NAME, system_prose, system_rules, system_status, show_rules, show_status
    )

    # if a control has no statement sub-parts then get the response bycomps from the imp_req itself
    # otherwise get them from the statements in the imp_req
    # an imp_req and a statement are both things that can have bycomps
    has_bycomps = imp_req.statements if imp_req.statements else [imp_req]
    for has_bycomp in has_bycomps:
        statement_id = getattr(has_bycomp, 'statement_id', f'{control_id}_smt')
        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(has_bycomp, write_empty_responses)

        if response_per_component or 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(1, title=header)
            for comp_name, comp_response in response_per_component.items():
                if comp_name == SSP_MAIN_COMP_NAME:
                    continue
                prose, rules, status = comp_response
                if show_comp:
                    SSPMarkdownWriter._write_component_prompt(
                        md_writer, comp_name, prose, rules, status, show_rules, show_status
                    )

    lines = md_writer.get_lines()

    tree = DocsMarkdownNode.build_tree_from_markdown(lines)
    tree.change_header_level_by(level)

    return tree.content.raw_text
get_control_statement(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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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(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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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(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
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def get_fedramp_control_tables(self, control_id: str, level: int, label_column: bool = 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(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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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(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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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(resolved_catalog) ¤

Set catalog.

Source code in trestle/core/ssp_io.py
54
55
56
57
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(ssp) ¤

Set ssp.

Source code in trestle/core/ssp_io.py
50
51
52
def set_ssp(self, ssp: ssp.SystemSecurityPlan) -> None:
    """Set ssp."""
    self._ssp = ssp

Functions¤

handler: python