35
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 | class SSPInheritanceAPI():
"""API for updating inheritance information in SSPs through inheritance markdown."""
def __init__(self, inheritance_md_path: pathlib.Path, trestle_root: pathlib.Path) -> None:
"""Initialize the SSP Inheritance API class."""
self._inheritance_markdown_path: pathlib.Path = inheritance_md_path
self._trestle_root: pathlib.Path = trestle_root
def write_inheritance_as_markdown(
self, leveraged_ssp_reference: str, catalog_api: Optional[CatalogAPI] = None
) -> None:
"""
Write inheritance information to markdown.
Args:
leveraged_ssp_reference: Location of the SSP to write inheritance information from.
catalog_api: Catalog API to filter inheritance information by catalog.
Notes:
If a catalog API is provided, the written controls in the markdown will be filtered by the catalog.
"""
leveraged_ssp: ossp.SystemSecurityPlan = self._fetch_leveraged_ssp(leveraged_ssp_reference)
if catalog_api is not None:
control_imp: ossp.ControlImplementation = leveraged_ssp.control_implementation
new_imp_requirements: List[ossp.ImplementedRequirement] = []
for imp_requirement in as_list(control_imp.implemented_requirements):
control = catalog_api._catalog_interface.get_control(imp_requirement.control_id)
if control is not None:
new_imp_requirements.append(imp_requirement)
control_imp.implemented_requirements = new_imp_requirements
leveraged_ssp.control_implementation = control_imp
export_writer: ExportWriter = ExportWriter(
self._inheritance_markdown_path, leveraged_ssp, leveraged_ssp_reference
)
export_writer.write_exports_as_markdown()
def update_ssp_inheritance(self, ssp: ossp.SystemSecurityPlan) -> None:
"""
Update inheritance information in SSP.
Args:
ssp: SSP to update with inheritance information.
"""
logger.debug('Reading inheritance information from markdown.')
reader = ExportReader(self._inheritance_markdown_path, ssp)
ssp = reader.read_exports_from_markdown()
leveraged_ssp_reference = reader.get_leveraged_ssp_href()
leveraged_ssp: ossp.SystemSecurityPlan = self._fetch_leveraged_ssp(leveraged_ssp_reference)
link: common.Link = common.Link(href=leveraged_ssp_reference)
leveraged_auths: List[ossp.LeveragedAuthorization] = []
leveraged_auth: ossp.LeveragedAuthorization = gens.generate_sample_model(ossp.LeveragedAuthorization)
leveraged_components: List[str] = reader.get_leveraged_components()
if not leveraged_components:
logger.warning(
'No leveraged components mapped to the SSP. '
'Please edit the inheritance markdown to include the leveraged authorization.'
)
else:
existing_leveraged_auth: ossp.LeveragedAuthorization = self._leveraged_auth_from_existing(
as_list(ssp.system_implementation.leveraged_authorizations), link
)
if existing_leveraged_auth is not None:
leveraged_auth = existing_leveraged_auth
else:
leveraged_auth.links = as_list(leveraged_auth.links)
leveraged_auth.links.append(link)
leveraged_auth.title = f'Leveraged Authorization for {leveraged_ssp.metadata.title}'
leveraged_auths.append(leveraged_auth)
# Overwrite the leveraged authorization in the SSP. The only leveraged authorization should be the one
# coming from inheritance view
ssp.system_implementation.leveraged_authorizations = none_if_empty(leveraged_auths)
self._reconcile_components(ssp, leveraged_ssp, leveraged_components, leveraged_auth)
def _fetch_leveraged_ssp(self, leveraged_ssp_reference: str) -> ossp.SystemSecurityPlan:
"""Fetch the leveraged SSP."""
leveraged_ssp: ossp.SystemSecurityPlan
fetcher = FetcherFactory.get_fetcher(self._trestle_root, leveraged_ssp_reference)
try:
leveraged_ssp, _ = fetcher.get_oscal()
except TrestleError as e:
raise TrestleError(f'Unable to fetch ssp from {leveraged_ssp_reference}: {e}')
return leveraged_ssp
def _reconcile_components(
self,
ssp: ossp.SystemSecurityPlan,
leveraged_ssp: ossp.SystemSecurityPlan,
leveraged_components: List[str],
leveraged_auth: ossp.LeveragedAuthorization
) -> None:
"""Reconcile components in the leveraging SSP with those in the leveraged SSP."""
mapped_components: Dict[str, ossp.SystemComponent] = {}
for component in as_list(leveraged_ssp.system_implementation.components):
if component.title in leveraged_components:
mapped_components[component.uuid] = component
new_components: List[ossp.SystemComponent] = []
for component in as_list(ssp.system_implementation.components):
props_dict: Dict[str, str] = {prop.name: prop.value for prop in as_list(component.props)}
# If this component is part of the original SSP components, add
# and continue
if const.LEV_AUTH_UUID not in props_dict:
new_components.append(component)
continue
# If the leveraged component already exists, update the title, description, type, and status
original_comp_uuid = props_dict[const.INHERITED_UUID]
if original_comp_uuid in mapped_components:
original_component = mapped_components.pop(original_comp_uuid)
self._update_leveraged_system_component(component, original_component, leveraged_auth.uuid)
new_components.append(component)
# Add any remaining components to the new components
for component in mapped_components.values():
new_component: ossp.SystemComponent = gens.generate_sample_model(ossp.SystemComponent)
self._update_leveraged_system_component(new_component, component, leveraged_auth.uuid)
logger.debug(f'Adding component {new_component.title} to components.')
new_components.append(new_component)
ssp.system_implementation.components = new_components
@staticmethod
def _update_leveraged_system_component(
new_comp: ossp.SystemComponent, original_comp: ossp.SystemComponent, leveraged_auth_id: str
) -> None:
"""Create a leveraged system component in the context of a leveraging system component."""
new_comp.type = original_comp.type
new_comp.title = original_comp.title
new_comp.description = original_comp.description
new_comp.status = original_comp.status
new_comp.props = [
common.Property(name=const.IMPLEMENTATION_POINT, value=const.IMPLEMENTATION_POINT_EXTERNAL),
common.Property(name=const.LEV_AUTH_UUID, value=leveraged_auth_id),
common.Property(name=const.INHERITED_UUID, value=original_comp.uuid)
]
def _leveraged_auth_from_existing(
self, leveraged_authorizations: List[ossp.LeveragedAuthorization], criteria_link: common.Link
) -> Optional[ossp.LeveragedAuthorization]:
"""Return the leveraged authorization if it is present in the ssp."""
for leveraged_auth in leveraged_authorizations:
if leveraged_auth.links and any(link.href == criteria_link.href for link in leveraged_auth.links):
return leveraged_auth
return None
|