Skip to content

Commit

Permalink
feat(entrypoint): adds create-ssp entrypoint and tests
Browse files Browse the repository at this point in the history
Signed-off-by: Jennifer Power <[email protected]>
  • Loading branch information
jpower432 committed Jan 30, 2024
1 parent 1f84696 commit dcb428c
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 33 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ trestlebot-autosync = "trestlebot.entrypoints.autosync:main"
trestlebot-rules-transform = "trestlebot.entrypoints.rule_transform:main"
trestlebot-create-cd = "trestlebot.entrypoints.create_cd:main"
trestlebot-sync-upstreams = "trestlebot.entrypoints.sync_upstreams:main"
trestlebot-create-ssp = "trestlebot.entrypoints.create_ssp:main"

[tool.poetry.dependencies]
python = '^3.8.1'
Expand Down
62 changes: 29 additions & 33 deletions tests/e2e/test_e2e_ssp.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
prepare_upstream_repo,
setup_for_ssp,
)
from trestlebot.const import ERROR_EXIT_CODE, INVALID_ARGS_EXIT_CODE, SUCCESS_EXIT_CODE
from trestlebot.tasks.authored.ssp import AuthoredSSP, SSPIndex
from trestlebot.const import ERROR_EXIT_CODE, SUCCESS_EXIT_CODE
from trestlebot.tasks.authored.ssp import SSPIndex


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -78,17 +78,6 @@
ERROR_EXIT_CODE,
True,
),
(
"failure/missing args",
{
"branch": "test",
"oscal-model": "ssp",
"committer-name": "test",
"committer-email": "[email protected]",
},
INVALID_ARGS_EXIT_CODE,
False,
),
],
)
def test_ssp_editing_e2e(
Expand All @@ -111,33 +100,40 @@ def test_ssp_editing_e2e(
tmp_repo_path = pathlib.Path(tmp_repo_str)

args = setup_for_ssp(tmp_repo_path, test_prof, [test_comp_name], test_ssp_md)
remote_url = "http://localhost:8080/test.git"
repo.create_remote("origin", url=remote_url)

# Create or generate the SSP
if not skip_create:
index_path = os.path.join(tmp_repo_str, "ssp-index.json")
ssp_index = SSPIndex(index_path)
authored_ssp = AuthoredSSP(tmp_repo_str, ssp_index)
authored_ssp.create_new_default(
create_args: Dict[str, str] = {
"markdown-path": command_args["markdown-path"],
"branch": command_args["branch"],
"committer-name": command_args["committer-name"],
"committer-email": command_args["committer-email"],
"ssp-name": test_ssp_name,
"profile-name": test_prof,
"compdefs": test_comp_name,
}
command = build_test_command(
tmp_repo_str, "create-ssp", create_args, image_name
)
run_response = subprocess.run(command, capture_output=True)
assert run_response.returncode == response
assert (tmp_repo_path / command_args["markdown-path"]).exists()

# Make a change to the SSP
ssp, ssp_path = ModelUtils.load_model_for_class(
tmp_repo_path,
test_ssp_name,
test_prof,
[test_comp_name],
test_ssp_md,
SystemSecurityPlan,
FileContentType.JSON,
)
ssp.metadata.title = "New Title"
ssp.oscal_write(ssp_path)
else:
ssp_generate = SSPGenerate()
assert ssp_generate._run(args) == 0

ssp_path: pathlib.Path = ModelUtils.get_model_path_for_name_and_class(
tmp_repo_path,
test_ssp_name,
SystemSecurityPlan,
FileContentType.JSON,
)
assert not ssp_path.exists()

remote_url = "http://localhost:8080/test.git"
repo.create_remote("origin", url=remote_url)

command = build_test_command(tmp_repo_str, "autosync", command_args, image_name)
run_response = subprocess.run(command, capture_output=True)
assert run_response.returncode == response
Expand All @@ -151,8 +147,8 @@ def test_ssp_editing_e2e(
)

# Check that the correct files are present with the correct content
assert (tmp_repo_path / command_args["markdown-path"]).exists()
ssp_index.reload()
index_path = os.path.join(tmp_repo_str, "ssp-index.json")
ssp_index = SSPIndex(index_path)
assert ssp_index.get_profile_by_ssp(test_ssp_name) == test_prof
assert ssp_index.get_comps_by_ssp(test_ssp_name) == [test_comp_name]
assert ssp_index.get_leveraged_by_ssp(test_ssp_name) is None
Expand Down
132 changes: 132 additions & 0 deletions trestlebot/entrypoints/create_ssp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/python

# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2024 Red Hat, Inc.

"""
Entrypoint for system security plan bootstrapping.
This will create and initial SSP with markdown, an SSP index, and a SSP JSON file.
"""

import argparse
import logging
import pathlib
from typing import List

from trestlebot.entrypoints.entrypoint_base import EntrypointBase, comma_sep_to_list
from trestlebot.entrypoints.log import set_log_level_from_args
from trestlebot.tasks.assemble_task import AssembleTask
from trestlebot.tasks.authored.ssp import AuthoredSSP, SSPIndex
from trestlebot.tasks.base_task import ModelFilter, TaskBase


logger = logging.getLogger(__name__)


class CreateSSPEntrypoint(EntrypointBase):
"""Entrypoint for ssp bootstrapping."""

def __init__(self, parser: argparse.ArgumentParser) -> None:
"""Initialize."""
super().__init__(parser)
self.setup_create_ssp_arguments()

def setup_create_ssp_arguments(self) -> None:
"""Setup specific arguments for this entrypoint."""
self.parser.add_argument(
"--ssp-name", required=True, type=str, help="Name of SSP to create."
)
self.parser.add_argument(
"--profile-name",
required=True,
help="Name of profile in the trestle workspace to include in the SSP.",
)
self.parser.add_argument(
"--compdefs",
required=True,
type=str,
help="Comma-separated list of component definitions to include in the SSP",
)
self.parser.add_argument(
"--leveraged-ssp",
required=False,
type=str,
help="Provider SSP to leverage for the new SSP.",
)
self.parser.add_argument(
"--markdown-path",
required=True,
type=str,
help="Path to create markdown files in.",
)
self.parser.add_argument(
"--yaml-header-path",
required=False,
type=str,
help="Optionally set a path to a YAML file for custom SSP Markdown YAML headers.",
)
self.parser.add_argument(
"--version",
required=False,
type=str,
help="Optionally set the SSP version.",
)
self.parser.add_argument(
"--ssp-index-path",
required=False,
type=str,
default="ssp-index.json",
help="Optionally set the path to the SSP index file.",
)

def run(self, args: argparse.Namespace) -> None:
"""Run the entrypoint."""

set_log_level_from_args(args)

# If the ssp index file does not exist, create it.
ssp_index_path = pathlib.Path(args.ssp_index_path)
if not ssp_index_path.exists():
# Create a parent directory
ssp_index_path.parent.mkdir(parents=True, exist_ok=True)

ssp_index = SSPIndex(args.ssp_index_path)
authored_ssp = AuthoredSSP(args.working_dir, ssp_index)

comps: List[str] = comma_sep_to_list(args.compdefs)
authored_ssp.create_new_default(
ssp_name=args.ssp_name,
profile_name=args.profile_name,
compdefs=comps,
markdown_path=args.markdown_path,
leveraged_ssp=args.leveraged_ssp,
yaml_header=args.yaml_header_path,
)

# The starting point for SSPs in the markdown, so assemble into JSON.
model_filter: ModelFilter = ModelFilter([], [args.ssp_name])
assemble_task = AssembleTask(
authored_object=authored_ssp,
markdown_dir=args.markdown_path,
version=args.version,
model_filter=model_filter,
)
pre_tasks: List[TaskBase] = [assemble_task]

super().run_base(args, pre_tasks)


def main() -> None:
"""Run the CLI."""
parser = argparse.ArgumentParser(
description="Create new system security plan for editing."
)
create_ssp = CreateSSPEntrypoint(parser=parser)

args = parser.parse_args()
create_ssp.run(args)


if __name__ == "__main__":
main()

0 comments on commit dcb428c

Please sign in to comment.