-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: populate cac content product name as component title for CPLYTM-380 and CPLYTM-381 #402
Changes from 2 commits
2ae3bb7
4343fe4
f3f26fc
810483f
cfa689d
f1e4148
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
product: ocp4 | ||
full_name: Red Hat OpenShift Container Platform 4 | ||
type: platform | ||
|
||
benchmark_id: OCP-4 | ||
benchmark_root: "../../applications" | ||
|
||
profiles_root: "./profiles" | ||
|
||
pkg_system: "rpm" | ||
|
||
init_system: "systemd" | ||
|
||
reference_uris: | ||
cis: 'https://www.cisecurity.org/benchmark/kubernetes/' | ||
stigid: 'https://public.cyber.mil/stigs/downloads/?_dl_facet_stigs=container-platform' | ||
|
||
cpes_root: "../../shared/applicability" | ||
cpes: | ||
- ocp4: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.1" | ||
title: "Red Hat OpenShift Container Platform 4" | ||
check_id: installed_app_is_ocp4 | ||
|
||
- ocp4-node: | ||
name: "cpe:/o:redhat:openshift_container_platform_node:4" | ||
title: "Red Hat OpenShift Container Platform 4 Node" | ||
check_id: installed_app_is_ocp4_node | ||
|
||
- ocp4-node-on-ovn: | ||
name: "cpe:/a:redhat:openshift_container_platform_node_on_ovn:4" | ||
title: "Red Hat OpenShift Container Platform 4 Node on OVN" | ||
check_id: installed_app_is_ocp4_node_on_openshift-ovn | ||
|
||
- ocp4-node-on-sdn: | ||
name: "cpe:/a:redhat:openshift_container_platform_node_on_sdn:4" | ||
title: "Red Hat OpenShift Container Platform 4 Node on SDN" | ||
check_id: installed_app_is_ocp4_node_on_openshift-sdn | ||
|
||
- ocp4.6: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.6" | ||
title: "Red Hat OpenShift Container Platform 4.6" | ||
check_id: installed_app_is_ocp4_6 | ||
|
||
- ocp4.7: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.7" | ||
title: "Red Hat OpenShift Container Platform 4.7" | ||
check_id: installed_app_is_ocp4_7 | ||
|
||
- ocp4.8: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.8" | ||
title: "Red Hat OpenShift Container Platform 4.8" | ||
check_id: installed_app_is_ocp4_8 | ||
|
||
- ocp4.9: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.9" | ||
title: "Red Hat OpenShift Container Platform 4.9" | ||
check_id: installed_app_is_ocp4_9 | ||
|
||
- ocp4.10: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.10" | ||
title: "Red Hat OpenShift Container Platform 4.10" | ||
check_id: installed_app_is_ocp4_10 | ||
|
||
- ocp4.11: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.11" | ||
title: "Red Hat OpenShift Container Platform 4.11" | ||
check_id: installed_app_is_ocp4_11 | ||
|
||
- ocp4.12: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.12" | ||
title: "Red Hat OpenShift Container Platform 4.12" | ||
check_id: installed_app_is_ocp4_12 | ||
|
||
- ocp4.13: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.13" | ||
title: "Red Hat OpenShift Container Platform 4.13" | ||
check_id: installed_app_is_ocp4_13 | ||
|
||
- ocp4.14: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.14" | ||
title: "Red Hat OpenShift Container Platform 4.14" | ||
check_id: installed_app_is_ocp4_14 | ||
|
||
- ocp4.15: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.15" | ||
title: "Red Hat OpenShift Container Platform 4.15" | ||
check_id: installed_app_is_ocp4_15 | ||
|
||
- ocp4.16: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.16" | ||
title: "Red Hat OpenShift Container Platform 4.16" | ||
check_id: installed_app_is_ocp4_16 | ||
|
||
- ocp4.17: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.17" | ||
title: "Red Hat OpenShift Container Platform 4.17" | ||
check_id: installed_app_is_ocp4_17 | ||
|
||
- ocp4.18: | ||
name: "cpe:/a:redhat:openshift_container_platform:4.18" | ||
title: "Red Hat OpenShift Container Platform 4.18" | ||
check_id: installed_app_is_ocp4_18 | ||
|
||
- ocp4-on-aws: | ||
name: "cpe:/a:redhat:openshift_container_platform_on_aws:4" | ||
title: "Red Hat OpenShift Container Platform 4 on AWS" | ||
check_id: installed_app_is_ocp4_on_aws | ||
|
||
- ocp4-on-azure: | ||
name: "cpe:/a:redhat:openshift_container_platform_on_azure:4" | ||
title: "Red Hat OpenShift Container Platform 4 on Azure" | ||
check_id: installed_app_is_ocp4_on_azure | ||
|
||
- ocp4-on-gcp: | ||
name: "cpe:/a:redhat:openshift_container_platform_on_gcp:4" | ||
title: "Red Hat OpenShift Container Platform 4 on GCP" | ||
check_id: installed_app_is_ocp4_on_gcp | ||
|
||
- ocp4-on-ovn: | ||
name: "cpe:/a:redhat:openshift_container_platform_on_ovn:4" | ||
title: "Red Hat OpenShift Container Platform 4 on OVN" | ||
check_id: installed_app_is_ocp4_on_openshiftovn | ||
|
||
- ocp4-on-sdn: | ||
name: "cpe:/a:redhat:openshift_container_platform_on_sdn:4" | ||
title: "Red Hat OpenShift Container Platform 4 on SDN" | ||
check_id: installed_app_is_ocp4_on_openshiftsdn | ||
|
||
|
||
# Requirement string, see: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#requirements-parsing | ||
# requires: "openscap>=1.3.4" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,16 @@ | |
from click.testing import CliRunner | ||
from git import Repo | ||
|
||
from tests.testutils import setup_for_catalog, setup_for_profile | ||
from trestlebot.cli.commands.sync_cac_content import sync_cac_content_cmd | ||
|
||
|
||
test_product = "ocp4" | ||
cac_content_test_data = pathlib.Path("tests/data/content").resolve() | ||
test_prof_path = pathlib.Path("tests/data/json/").resolve() | ||
test_prof = "simplified_nist_profile" | ||
test_cat = "simplified_nist_catalog" | ||
test_comp_path = "component-definitions/cac-components/component-definition.json" | ||
|
||
|
||
def test_missing_required_option(tmp_repo: Tuple[str, Repo]) -> None: | ||
|
@@ -37,3 +43,46 @@ def test_missing_required_option(tmp_repo: Tuple[str, Repo]) -> None: | |
], | ||
) | ||
assert result.exit_code == 2 | ||
|
||
|
||
def test_sync_product_name(tmp_repo: Tuple[str, Repo]) -> None: | ||
"""Tests sync Cac content product name to OSCAL component title .""" | ||
repo_dir, _ = tmp_repo | ||
repo_path = pathlib.Path(repo_dir) | ||
setup_for_catalog(repo_path, test_cat, "catalog") | ||
setup_for_profile(repo_path, test_prof, "profile") | ||
|
||
runner = CliRunner() | ||
result = runner.invoke( | ||
sync_cac_content_cmd, | ||
[ | ||
"--product", | ||
test_product, | ||
"--repo-path", | ||
str(repo_path.resolve()), | ||
"--cac-content-root", | ||
cac_content_test_data, | ||
"--cac-profile", | ||
"cac-profile", | ||
"--oscal-profile", | ||
test_prof, | ||
"--committer-email", | ||
"[email protected]", | ||
"--committer-name", | ||
"test name", | ||
"--markdown-dir", | ||
"markdown", | ||
"--branch", | ||
"test", | ||
"--dry-run", | ||
], | ||
) | ||
# Check the CLI sync-cac-content is successful | ||
assert result.exit_code == 0 | ||
# Check if the component definition is created | ||
component_definition = repo_path.joinpath(test_comp_path) | ||
assert component_definition.exists() | ||
# Check if it populates the product name as the component title | ||
with open(component_definition, "r", encoding="utf-8") as file: | ||
content = file.read() | ||
assert '"title": "ocp4"' in content |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,21 @@ | |
|
||
"""Module for sync cac content command""" | ||
import logging | ||
from typing import Any | ||
from typing import Any, List | ||
|
||
import click | ||
|
||
from trestlebot import const | ||
from trestlebot.cli.options.common import common_options, git_options, handle_exceptions | ||
from trestlebot.cli.utils import get_component_title, run_bot | ||
from trestlebot.tasks.authored.compdef import ( | ||
AuthoredComponentDefinition, | ||
FilterByProfile, | ||
) | ||
from trestlebot.tasks.base_task import ModelFilter, TaskBase | ||
from trestlebot.tasks.regenerate_task import RegenerateTask | ||
from trestlebot.tasks.rule_transform_task import RuleTransformTask | ||
from trestlebot.transformers.yaml_transformer import ToRulesYAMLTransformer | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
@@ -50,11 +60,77 @@ | |
required=False, | ||
default="service", | ||
) | ||
@click.option( | ||
"--markdown-dir", | ||
type=str, | ||
help="Directory name to store markdown files.", | ||
required=True, | ||
) | ||
@handle_exceptions | ||
def sync_cac_content_cmd(ctx: click.Context, **kwargs: Any) -> None: | ||
"""Transform CaC content to OSCAL component definition.""" | ||
|
||
# Steps: | ||
# 1. Check options, logger errors if any and exit. | ||
# 2. Create a new task to run the data transformation. | ||
# 3. Initialize a Trestlebot object and run the task(s). | ||
|
||
pre_tasks: List[TaskBase] = [] | ||
|
||
oscal_profile = kwargs["oscal_profile"] | ||
compdef_name = "cac-components" | ||
product = kwargs["product"] | ||
cac_content_root = kwargs["cac_content_root"] | ||
component_title = get_component_title(product, cac_content_root) | ||
component_description = kwargs["product"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking but since we are reading the product properties, we could use the full_name as description. This can be done in a separate PR anyways. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated the component description, and it works. But it can't pass the test with python 3.8. The reason is that Python 3.8 and earlier used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed the test error. |
||
filter_by_profile = kwargs.get("filter_by_profile") | ||
component_definition_type = kwargs.get("component_definition_type", "service") | ||
markdown_dir = kwargs["markdown_dir"] | ||
repo_path = kwargs["repo_path"] | ||
if filter_by_profile: | ||
filter_by_profile = FilterByProfile(repo_path, filter_by_profile) | ||
authored_comp: AuthoredComponentDefinition = AuthoredComponentDefinition( | ||
trestle_root=repo_path, | ||
) | ||
authored_comp.create_new_default( | ||
profile_name=oscal_profile, | ||
compdef_name=compdef_name, | ||
comp_title=component_title, | ||
comp_description=component_description, | ||
comp_type=component_definition_type, | ||
filter_by_profile=filter_by_profile, | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this to create a new component definition directly? I saw an AC item of CPLYTM-217:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it will create a new component definition. I think the AC is out of the CPLYTM-217 scope. It seems for whole EPIC. Also, the Fourth AC is not for CPLYTM-217.
If there is an existing component definition in the workplace, I have no the component's uuid and title mapping, it seems that the component element can't be obtained correctly. It's difficult to say the component exists. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have thought about this issue again. It should be updated. But I don't know how to update it at that moment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a new function to update the existing component definition. |
||
logger.info(f"Component definition name is: {component_title}.") | ||
|
||
transformer: ToRulesYAMLTransformer = ToRulesYAMLTransformer() | ||
|
||
model_filter: ModelFilter = ModelFilter( | ||
[], [compdef_name, component_title, f"{const.RULE_PREFIX}*"] | ||
) | ||
logger.info(f"model_filter is: {model_filter}.") | ||
|
||
rule_transform_task: RuleTransformTask = RuleTransformTask( | ||
working_dir=repo_path, | ||
rules_view_dir=const.RULES_VIEW_DIR, | ||
rule_transformer=transformer, | ||
model_filter=model_filter, | ||
) | ||
logger.info( | ||
f"Profile to filter controls in the component files is: {filter_by_profile}." | ||
) | ||
logger.debug( | ||
f"Oscal profile in use with the component definition is: {oscal_profile}." | ||
) | ||
logger.debug(f"Component definition type is {component_definition_type}.") | ||
|
||
pre_tasks.append(rule_transform_task) | ||
|
||
regenerate_task: RegenerateTask = RegenerateTask( | ||
authored_object=authored_comp, | ||
markdown_dir=markdown_dir, | ||
model_filter=model_filter, | ||
) | ||
pre_tasks.append(regenerate_task) | ||
|
||
run_bot(pre_tasks, kwargs) | ||
|
||
logger.debug(f"You have successfully authored the {compdef_name}.") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,19 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# Copyright (c) 2024 Red Hat, Inc. | ||
|
||
import logging | ||
from typing import Any, Dict, List | ||
|
||
from ssg.products import load_product_yaml, product_yaml_path | ||
|
||
from trestlebot.bot import TrestleBot | ||
from trestlebot.reporter import BotResults | ||
from trestlebot.tasks.base_task import TaskBase | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def comma_sep_to_list(string: str) -> List[str]: | ||
"""Convert comma-sep string to list of strings and strip.""" | ||
string = string.strip() if string else "" | ||
|
@@ -35,3 +41,16 @@ def run_bot(pre_tasks: List[TaskBase], kwargs: Dict[Any, Any]) -> BotResults: | |
), | ||
dry_run=kwargs.get("dry_run", False), | ||
) | ||
|
||
|
||
def get_component_title(product_name: str, cac_path: str) -> str: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is for CaC content specially, utils module is used for common functions for cli commands. Since there are following work based on this to attach component props, how about moving to a transformation workflow? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. How about move it to a new file trestlebot/transformers/cac_transformer.py? |
||
"""Get the product name from product yml file via the SSG library.""" | ||
if product_name and cac_path: | ||
# Get the product yaml file path | ||
product_yml_path = product_yaml_path(cac_path, product_name) | ||
# Load the product data | ||
product = load_product_yaml(product_yml_path) | ||
# Return product name from product yml file | ||
return product._primary_data.get("product") | ||
else: | ||
raise ValueError("component_title is empty or None") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the ssg guide, it recommends to use a stable version, it can change unexpectedly thus unstable when installing from master. e.g.,
`https://github.com/ComplianceasCode/[email protected]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. The latest tag of content is v0.1.75, released on 2024 Nov 11. We need the latest SSG extension in the master. Let's keep it now. It could be updated when the new tag contains the latest changes.