Skip to content
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: task cis-xlsx-to-oscal-cd #1764

Merged
merged 38 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
80a804f
task cis-xlsx-to-oscal-cd
degenaro Dec 2, 2024
1a6ef02
docs
degenaro Dec 2, 2024
5d9fd7f
Merge branch 'develop' into fix/cisb-to-oscal-cd
degenaro Dec 3, 2024
4a869a5
Merge branch 'develop' into fix/cisb-to-oscal-cd
degenaro Dec 3, 2024
17fbd01
signatures & trace
degenaro Dec 3, 2024
00cc192
tmpdir
degenaro Dec 3, 2024
574a40e
Merge branch 'develop' into fix/cisb-to-oscal-cd
degenaro Dec 3, 2024
e957eca
hack
degenaro Dec 3, 2024
ac86204
Merge branch 'fix/cisb-to-oscal-cd' of https://github.com/oscal-compa…
degenaro Dec 3, 2024
f5ce4e7
give up.
degenaro Dec 3, 2024
adc2a4c
Fix failing macos pipeline (thanks Vikas!)
degenaro Dec 4, 2024
709bb07
SortHelper
degenaro Dec 4, 2024
28f6845
Add debugging try/except
degenaro Dec 4, 2024
1922968
2nd try
degenaro Dec 4, 2024
e2e6a6e
add utf-8 encoding to open
degenaro Dec 4, 2024
bb36e1b
Fix sonar code smells
degenaro Dec 4, 2024
2719422
Fix sonar code smells
degenaro Dec 4, 2024
e236d96
Fix sonar code smells
degenaro Dec 4, 2024
22f11b1
Fix sonar code smells
degenaro Dec 4, 2024
fbf822a
Fix sonar code smells
degenaro Dec 4, 2024
dcec52f
Fix sonar code smells
degenaro Dec 4, 2024
503c612
Fix sonar code smells
degenaro Dec 4, 2024
ea7ee72
Fix sonar code smells
degenaro Dec 4, 2024
8ae4c95
Merge branch 'develop' into fix/cisb-to-oscal-cd
degenaro Dec 4, 2024
e8e1864
improve test spot checking
degenaro Dec 5, 2024
56e63ea
Merge branch 'develop' into fix/cisb-to-oscal-cd
degenaro Jan 2, 2025
34bb023
tests: bad_confg and bad_overwrite
degenaro Jan 2, 2025
c3a4889
simplify & improve test coverage
degenaro Jan 3, 2025
a888d68
simplify & improve test coverage
degenaro Jan 3, 2025
be67c3e
simplify & improve test coverage
degenaro Jan 3, 2025
cc5eda9
simplify & improve test coverage
degenaro Jan 3, 2025
6b54603
simplify & improve test coverage
degenaro Jan 3, 2025
4d47d48
simplify & improve test coverage
degenaro Jan 6, 2025
3997788
simplify & improve test coverage
degenaro Jan 6, 2025
79dfc1f
Simplify & improve test coverage
degenaro Jan 7, 2025
8d8647e
Simplify & improve test coverage
degenaro Jan 7, 2025
8edc4e6
100% test coverage!
degenaro Jan 7, 2025
6a508fd
Fix license.
degenaro Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/reference/API/trestle/tasks/cis_xlsx_to_oscal_cd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: trestle.tasks.cis_xlsx_to_oscal_cd
description: Documentation for trestle.tasks.cis_xlsx_to_oscal_cd module
---

::: trestle.tasks.cis_xlsx_to_oscal_cd
handler: python
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[task.cis-xlsx-to-oscal-cd]

benchmark-file = tests/data/tasks/cis-xlsx-to-oscal-cd/CIS_IBM_Db2_11_Benchmark_v1.1.0.snippet.xlsx
benchmark-title = CIS IBM Db2 11 Benchmark
benchmark-version = 1.1.0

namespace = https://oscal-compass/compliance-trestle/schemas/oscal/cd

component-name = IBM Db2 11
component-description = IBM Db2 11
component-type = software

profile-version = v8
profile-source = catalogs/CIS_controls_v8/catalog.json
profile-description = CIS catalog v8

output-dir = tests/data/tasks/cis-xlsx-to-oscal-cd/output
output-overwrite = true
8 changes: 8 additions & 0 deletions tests/trestle/core/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from trestle.common.err import TrestleError
from trestle.common.model_utils import ModelUtils
from trestle.common.str_utils import AliasMode
from trestle.common.str_utils import as_bool


def load_good_catalog() -> catalog.Catalog:
Expand Down Expand Up @@ -352,3 +353,10 @@ def test_prune_empty_dirs(tmp_path: pathlib.Path) -> None:
assert not (tmp_path / 'sub1/sub11/sub111').exists()
assert foo_path.exists()
assert bar_path.exists()


def test_as_bool(tmp_path: pathlib.Path) -> None:
"""Test as_bool function."""
assert as_bool('true')
assert not as_bool('false')
assert not as_bool(None)
242 changes: 242 additions & 0 deletions tests/trestle/tasks/cis_xlsx_to_oscal_cd_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Copyright (c) 2025 The OSCAL Compass Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""cis-xlsx-to-oscal-cd task tests."""

import configparser
import os
import pathlib
from typing import Dict
from unittest.mock import patch

import trestle.tasks.cis_xlsx_to_oscal_cd as cis_xlsx_to_oscal_cd
from trestle.oscal.component import ComponentDefinition
from trestle.tasks.base_task import TaskOutcome

db2_config = 'tests/data/tasks/cis-xlsx-to-oscal-cd/test-cis-xlsx-to-oscal-cd.db2.snippet.config'


def _get_section(tmp_path: pathlib.Path, file_: str) -> Dict:
"""Get section."""
config = configparser.ConfigParser()
config_path = pathlib.Path(file_)
config.read(config_path)
section = config['task.cis-xlsx-to-oscal-cd']
section['output-dir'] = str(tmp_path)
return section


def test_cis_xlsx_to_oscal_cd_compare(tmp_path: pathlib.Path):
"""Test compare."""
x = cis_xlsx_to_oscal_cd.SortHelper.compare('A', 'B')
assert x == -1
x = cis_xlsx_to_oscal_cd.SortHelper.compare('0.0', '1.0')
assert x == -1
x = cis_xlsx_to_oscal_cd.SortHelper.compare('1.0', '0.0')
assert x == 1
x = cis_xlsx_to_oscal_cd.SortHelper.compare('1.1', '1.1')
assert x == 0


def test_cis_xlsx_to_oscal_cd_print_info(tmp_path: pathlib.Path):
"""Test print_info call."""
section = _get_section(tmp_path, db2_config)
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.print_info()
assert retval is None


def test_cis_xlsx_to_oscal_cd_simulate(tmp_path: pathlib.Path):
"""Test simulate call."""
section = _get_section(tmp_path, db2_config)
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.simulate()
assert retval == TaskOutcome.SIM_SUCCESS
assert len(os.listdir(str(tmp_path))) == 0


def test_cis_xlsx_to_oscal_cd_execute(tmp_path: pathlib.Path):
"""Test execute call - db2."""
section = _get_section(tmp_path, db2_config)
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_combined(tmp_path: pathlib.Path):
"""Test execute call - db2."""
section = _get_section(tmp_path, db2_config)
section['benchmark-file'
] = 'tests/data/tasks/cis-xlsx-to-oscal-cd/CIS_IBM_Db2_11_Benchmark_v1.1.0.snippet_combined.xlsx'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_missing_column(tmp_path: pathlib.Path):
"""Test execute call - missing column."""
section = _get_section(tmp_path, db2_config)
section['benchmark-file'
] = 'tests/data/tasks/cis-xlsx-to-oscal-cd/CIS_IBM_Db2_11_Benchmark_v1.1.0.snippet_missing_column.xlsx'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_bad_config(tmp_path: pathlib.Path):
"""Test execute call - bad config."""
section = _get_section(tmp_path, db2_config)
del section['benchmark-file']
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_bad_overwrite(tmp_path: pathlib.Path):
"""Test execute call - bad overwrite."""
section = _get_section(tmp_path, db2_config)
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
section['output-overwrite'] = 'false'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_merge(tmp_path: pathlib.Path):
"""Test execute call - merge."""
section = _get_section(tmp_path, db2_config)
section['benchmark-file'
] = 'tests/data/tasks/cis-xlsx-to-oscal-cd/CIS_IBM_Db2_11_Benchmark_v1.1.0.snippet_merge.xlsx'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_rule_prefix(tmp_path: pathlib.Path):
"""Test execute call - rule prefix."""
section = _get_section(tmp_path, db2_config)
section['benchmark-rule-prefix'] = 'CIS'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_control_prefix(tmp_path: pathlib.Path):
"""Test execute call - control prefix."""
section = _get_section(tmp_path, db2_config)
section['benchmark-control-prefix'] = 'cisc'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_control_bad(tmp_path: pathlib.Path):
"""Test execute call - control bad."""
section = _get_section(tmp_path, db2_config)
section['benchmark-file'
] = 'tests/data/tasks/cis-xlsx-to-oscal-cd/CIS_IBM_Db2_11_Benchmark_v1.1.0.snippet_bad_control.xlsx'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_columns_exclude(tmp_path: pathlib.Path):
"""Test execute call - control prefix."""
section = _get_section(tmp_path, db2_config)
section['columns-exclude'] = '"Recommendation #", "Profile", "Description"'
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.SUCCESS
_validate_db2(tmp_path)


def test_cis_xlsx_to_oscal_cd_execute_config_missing(tmp_path: pathlib.Path):
"""Test execute call - config_missing."""
section = None
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_count_mismatch(tmp_path: pathlib.Path):
"""Test execute call - count mismatch."""
with patch('trestle.tasks.cis_xlsx_to_oscal_cd.CombineHelper._populate_combined_map') as mock_original_function:
mock_original_function.return_value = -5
section = _get_section(tmp_path, db2_config)
tgt = cis_xlsx_to_oscal_cd.CisXlsxToOscalCd(section)
retval = tgt.execute()
assert retval == TaskOutcome.FAILURE


def test_cis_xlsx_to_oscal_cd_execute_csv_row_mgr(tmp_path: pathlib.Path):
"""Test execute call - csv row mgr."""
row_names = ['row1', 'row2', 'row3']
# create new row mgr
csv_row_mgr = cis_xlsx_to_oscal_cd.CsvRowMgr(row_names)
# test valid case
try:
csv_row_mgr.put('row1', '')
except RuntimeError:
assert 0 == 1
# test invalid case
try:
csv_row_mgr.put('rowX', '')
assert 0 == 1
except RuntimeError:
pass


def _validate_db2(tmp_path: pathlib.Path):
"""Validate produced OSCAL for db2 cd."""
# read catalog
file_path = tmp_path / 'component-definition.json'
component_definition = ComponentDefinition.oscal_read(file_path)
# spot check
assert len(component_definition.components) == 1
assert component_definition.metadata.title == 'CIS IBM Db2 11 Benchmark'
assert component_definition.metadata.version == '1.1.0'
component = component_definition.components[0]
assert component.type == 'software'
assert len(component.props) == 552
prop = component.props[0]
assert prop.name == 'Rule_Id'
assert prop.ns == 'https://oscal-compass/compliance-trestle/schemas/oscal/cd'
assert prop.value == 'CIS-1.1.1'
assert prop.remarks == 'rule_set_00'
assert len(component.control_implementations) == 1
prop = component.props[551]
assert prop.name == 'Group_Description_Level_1'
assert prop.ns == 'https://oscal-compass/compliance-trestle/schemas/oscal/cd'
assert prop.value.startswith('This section provides guidance on various database configuration parameters.')
assert prop.remarks == 'rule_set_22'
assert len(component.control_implementations) == 1
control_implementation = component.control_implementations[0]
assert len(control_implementation.implemented_requirements) == 6
assert control_implementation.source == 'catalogs/CIS_controls_v8/catalog.json'
assert control_implementation.description == 'CIS catalog v8'
implemented_requirement = control_implementation.implemented_requirements[0]
assert implemented_requirement.control_id == 'cisc-7.4'
assert len(implemented_requirement.props) == 1
prop = implemented_requirement.props[0]
assert prop.name == 'Rule_Id'
assert prop.ns == 'https://oscal-compass/compliance-trestle/schemas/oscal/cd'
assert prop.value == 'CIS-1.1.1'
implemented_requirement = control_implementation.implemented_requirements[5]
assert implemented_requirement.control_id == 'cisc-3.10'
12 changes: 12 additions & 0 deletions trestle/common/str_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ def as_string(string_or_none: Optional[str]) -> str:
return string_or_none if string_or_none else ''


def as_bool(string_or_none: Optional[str]) -> bool:
"""Convert string to boolean."""
if string_or_none:
if string_or_none.lower() in ['false']:
rval = False
else:
rval = True
else:
rval = False
return rval


def string_from_root(item_with_root: Optional[Any]) -> str:
"""Convert root to string if present."""
return as_string(item_with_root.__root__) if item_with_root else ''
Expand Down
Loading
Loading