Skip to content

Commit

Permalink
fix: adding parameter aggregation from other parameter values for giv…
Browse files Browse the repository at this point in the history
…en control (#1412)

* feat: adding parameter aggregation functionality

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

* fix: fix code complexity

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

* fix: reduce code complexity

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

* fix: add parameters for test cases and reduce code complexity

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

* fix: fixing tests merge

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

* fix: correct code lint

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>

---------

Signed-off-by: Alejandro Jose Leiva Palomo <alejandro.leiva.palomo@ibm.com>
  • Loading branch information
AleJo2995 committed Jul 4, 2023
1 parent dbfc1d6 commit 7b8cad0
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 5 deletions.
56 changes: 54 additions & 2 deletions tests/trestle/core/commands/author/profile_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def test_profile_generate_assemble(
if set_parameters_flag:
assert set_params[2].values[0] == 'new value'
assert set_params[1].props[0].ns == const.TRESTLE_GENERIC_NS
assert len(set_params) == 15
assert len(set_params) == 18
else:
# the original profile did not have ns set for this display name
# confirm the namespace is not defined unless set_parameters_flag is True
Expand Down Expand Up @@ -381,7 +381,7 @@ def test_profile_ohv(required_sections: Optional[str], success: bool, ohv: bool,
)
set_params = profile.modify.set_parameters

assert len(set_params) == 15
assert len(set_params) == 18
assert set_params[0].values[0] == 'all personnel'
# the label is present in the header so it ends up in the set_parameter
assert set_params[0].label == 'label from edit'
Expand Down Expand Up @@ -1118,6 +1118,58 @@ def test_profile_inherit(tmp_trestle_dir: pathlib.Path):
assert prof_inherit._run(args) == 2


def test_profile_generate_assemble_parameter_aggregation(
tmp_trestle_dir: pathlib.Path, monkeypatch: MonkeyPatch
) -> None:
"""Test the profile markdown generator."""
_, assembled_prof_dir, _, markdown_path = setup_profile_generate(tmp_trestle_dir, 'simple_test_profile.json')
yaml_header_path = test_utils.YAML_TEST_DATA_PATH / 'good_simple.yaml'
ac_path = markdown_path / 'ac'

nist_cat, _ = ModelUtils.load_model_for_class(tmp_trestle_dir, 'nist_cat', cat.Catalog, FileContentType.JSON)

appended_prop = {'name': 'aggregates', 'value': 'at-02_odp.01'}
ac_1 = nist_cat.groups[0].controls[0]
ac_1.params[2].props = []
ac_1.params[2].props.append(appended_prop)
appended_extra_param = {
'id': 'at-02_odp.01',
'props': [{
'name': 'label', 'value': 'AT-02_ODP[01]', 'class': 'sp800-53a'
}],
'label': 'frequency',
'guidelines': [{
'prose': 'blah'
}]
}
ac_1.params.append(appended_extra_param)

ModelUtils.save_top_level_model(nist_cat, tmp_trestle_dir, 'nist_cat', FileContentType.JSON)

# convert resolved profile catalog to markdown then assemble it after adding an item to a control
# generate, edit, assemble
test_args = f'trestle author profile-generate -n {prof_name} -o {md_name} -rs NeededExtra'.split( # noqa E501
)
test_args.extend(['-y', str(yaml_header_path)])
test_args.extend(['-s', all_sections_str])
monkeypatch.setattr(sys, 'argv', test_args)

assert Trestle().run() == 0

fc = test_utils.FileChecker(ac_path)

assert Trestle().run() == 0

assert fc.files_unchanged()

# assemble based on set_parameters_flag
test_args = f'trestle author profile-assemble -n {prof_name} -m {md_name} -o {assembled_prof_name}'.split()
test_args.append('-sp')
assembled_prof_dir.mkdir()
monkeypatch.setattr(sys, 'argv', test_args)
assert Trestle().run() == 0


def test_profile_generate_assesment_objectives(tmp_trestle_dir: pathlib.Path, monkeypatch: MonkeyPatch) -> None:
"""Test the profile markdown generator."""
_, _, _, _ = setup_profile_generate(tmp_trestle_dir, 'simple_test_profile.json')
Expand Down
4 changes: 2 additions & 2 deletions trestle/core/catalog/catalog_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def read_additional_content(
# if profile_values are present, overwrite values with them
if const.PROFILE_VALUES in param_dict:
param_dict[const.VALUES] = param_dict.pop(const.PROFILE_VALUES)
final_param_dict[param_id] = param_dict
param_sort_map[param_id] = sort_id
final_param_dict[param_id] = param_dict
param_sort_map[param_id] = sort_id
new_alters: List[prof.Alter] = []
# fill the alters according to the control sorting order
for key in sorted(alters_map.keys()):
Expand Down
13 changes: 12 additions & 1 deletion trestle/core/catalog/catalog_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import trestle.common.const as const
import trestle.oscal.catalog as cat
from trestle.common.list_utils import as_list, deep_get, none_if_empty
from trestle.common.list_utils import as_list, deep_get, delete_list_from_list, none_if_empty
from trestle.common.model_utils import ModelUtils
from trestle.core.catalog.catalog_interface import CatalogInterface
from trestle.core.catalog.catalog_merger import CatalogMerger
Expand Down Expand Up @@ -63,6 +63,17 @@ def write_catalog_as_profile_markdown(

# get all params and vals for this control from the resolved profile catalog with block adds in effect
control_param_dict = ControlInterface.get_control_param_dict(control, False)
to_delete = []
# removes aggregate parameters to be non-editable in markdowns
props_by_param_ids = {}
for param_id, values_dict in control_param_dict.items():
props_by_param_ids[param_id] = [
prop for prop in as_list(values_dict.props) if prop.name == 'aggregates'
]
to_delete = list({k: v for k, v in props_by_param_ids.items() if v != []}.keys())
unique_params_to_del = list(set(to_delete))
if unique_params_to_del:
delete_list_from_list(control_param_dict, unique_params_to_del)

set_param_dict = self._construct_set_parameters_dict(profile_set_param_dict, control_param_dict, context)

Expand Down
35 changes: 35 additions & 0 deletions trestle/core/commands/author/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def generate_markdown(
catalog, inherited_props = ProfileResolver().get_resolved_profile_catalog_and_inherited_props(
trestle_root, profile_path, True, True, None, ParameterRep.LEAVE_MOUSTACHE
)

deep_set(yaml_header, [const.TRESTLE_GLOBAL_TAG, const.PROFILE, const.TITLE], profile.metadata.title)

context = ControlContext.generate(ContextPurpose.PROFILE, True, trestle_root, markdown_path)
Expand Down Expand Up @@ -284,6 +285,33 @@ def _replace_modify_set_params(
profile.modify.set_parameters = none_if_empty(profile.modify.set_parameters)
return changed

@staticmethod
def _add_aggregated_parameter(
param: Any, param_dict: Dict[str, Any], control_id: str, controls: Any, param_map: Dict[str, str]
) -> None:
"""
Add aggregated parameter value to original parameter.
Notes:
None
"""
# verifies aggregated param is not on grabbed param dict
if param.id not in list(param_dict.keys()):
param.values = []
agg_props = [prop for prop in param.props if prop.name == 'aggregates']
for prop in as_list(agg_props):
if param_dict[prop.value].get('values'):
agg_param_values = param_dict[prop.value].get('values')
for value in as_list(agg_param_values):
param.values.append(value)
else:
agg_param_values = [p for p in controls[control_id] if p.id == prop.value][0]
param.values.append(agg_param_values.props[0].value)
dict_param = ModelUtils.parameter_to_dict(param, False)
param_dict[param.id] = dict_param
param_map[param.id] = control_id
return None

@staticmethod
def assemble_profile(
trestle_root: pathlib.Path,
Expand Down Expand Up @@ -353,6 +381,13 @@ def assemble_profile(
catalog_api = CatalogAPI(catalog=catalog, context=context)
found_alters, param_dict, param_map = catalog_api.read_additional_content_from_md(label_as_key=True)

controls = {}
for group in as_list(catalog.groups):
for control in as_list(group.controls):
controls[control.id] = control.params
for control_id, params in controls.items():
for param in as_list(params):
ProfileAssemble._add_aggregated_parameter(param, param_dict, control_id, controls, param_map)
# technically if allowed sections is [] it means no sections are allowed
if allowed_sections is not None:
for bad_part in [
Expand Down

0 comments on commit 7b8cad0

Please sign in to comment.