diff --git a/trestlebot/cli/commands/sync_cac_content.py b/trestlebot/cli/commands/sync_cac_content.py index 16f3d37b..75d62f34 100644 --- a/trestlebot/cli/commands/sync_cac_content.py +++ b/trestlebot/cli/commands/sync_cac_content.py @@ -53,7 +53,7 @@ ) @click.option( "--component-definition-type", - type=click.Choice(["service", "validation"]), + type=click.Choice(["service", "validation", "software"]), help="Type of component definition. Default: service", required=False, default="service", diff --git a/trestlebot/tasks/sync_cac_content_task.py b/trestlebot/tasks/sync_cac_content_task.py index 2603a05b..afdb0d09 100644 --- a/trestlebot/tasks/sync_cac_content_task.py +++ b/trestlebot/tasks/sync_cac_content_task.py @@ -24,6 +24,7 @@ RuleInfo, RulesTransformer, get_component_info, + get_validation_component_mapping, ) @@ -84,25 +85,43 @@ def _create_or_update_compdef(self, compdef_type: str = "service") -> None: product_name, full_name = get_component_info( self.product, self.cac_content_root ) - oscal_component.title = product_name - oscal_component.type = compdef_type - oscal_component.description = full_name all_rule_properties = self._get_rules_properties() - oscal_component.props = none_if_empty(all_rule_properties) - + props = none_if_empty(all_rule_properties) + oscal_component.type = self.compdef_type + if oscal_component.type == "validation": + oscal_component.title = "openscap" + oscal_component.description = "openscap" + oscal_component.props = get_validation_component_mapping(props) + else: + oscal_component.title = product_name + oscal_component.description = full_name + oscal_component.props = props repo_path = pathlib.Path(self.working_dir) cd_dir = repo_path.joinpath(f"{trestle_const.MODEL_DIR_COMPDEF}/{self.product}") cd_json = cd_dir / "component-definition.json" if cd_json.exists(): logger.info(f"The component definition for {self.product} exists.") compdef = ComponentDefinition.oscal_read(cd_json) + components_titles = [] updated = False for index, component in enumerate(compdef.components): + components_titles.append(component.title) + # If the component exists and the props need to be updated if component.title == oscal_component.title: if component.props != oscal_component.props: + logger.info( + f"Start to update the props of the component {component.title}" + ) compdef.components[index].props = oscal_component.props updated = True + compdef.oscal_write(cd_json) break + # If the component doesn't exist, append this component + if oscal_component.title not in components_titles: + logger.info(f"Start to append the component {oscal_component.title}") + compdef.components.append(oscal_component) + compdef.oscal_write(cd_json) + updated = True if updated: logger.info(f"Update component definition: {cd_json}") compdef.metadata.version = str( diff --git a/trestlebot/transformers/cac_transformer.py b/trestlebot/transformers/cac_transformer.py index 637850a1..92c704c8 100644 --- a/trestlebot/transformers/cac_transformer.py +++ b/trestlebot/transformers/cac_transformer.py @@ -35,6 +35,45 @@ def get_component_info(product_name: str, cac_path: str) -> Tuple[str, str]: raise ValueError("component_title is empty or None") +def get_validation_component_mapping( + props: List[Dict[str, str]] +) -> List[Dict[str, str]]: + """ + Adds a new "Check_Id" and "Check_Description" to the props based on the + "Rule_Id" value and "Rule_Description". + + Args: + props (List[Dict]): The input list of dictionaries. + + Returns: + List[Dict]: The updated list with the new "Check_Id" and + "Check_Description" entry. + """ + props = props + rule_check_mapping = [] + check_id_entry = {} + for prop in props: + if prop["name"] == "Rule_Id": + rule_check_mapping.append(prop) + check_id_entry = { + "name": "Check_Id", + "ns": prop["ns"], + "value": prop["value"], + "remarks": prop["remarks"], + } + if prop["name"] == "Rule_Description": + rule_check_mapping.append(prop) + rule_check_mapping.append(check_id_entry) + check_description_entry = { + "name": "Check_Description", + "ns": prop["ns"], + "value": prop["value"], + "remarks": prop["remarks"], + } + rule_check_mapping.append(check_description_entry) + return rule_check_mapping + + def add_prop(name: str, value: str, remarks: Optional[str] = None) -> Property: """Add a property to a set of rule properties.""" prop = generate_sample_model(Property)