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

fix: improve bad property error message by including csv row number #1466

Merged
merged 5 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 66 additions & 0 deletions tests/trestle/tasks/csv_to_oscal_cd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,3 +1138,69 @@ def test_execute_validation(tmp_path: pathlib.Path) -> None:
assert component.props[2].name == 'Check_Description'
assert component.props[2].value == 'validation-check-description'
assert len(component.control_implementations) == 0


def test_row_property_builder(tmp_path):
"""Test row property builder."""
# valid
prop = csv_to_oscal_cd.row_property_builder(
row=0,
name='name',
value='value',
ns='https://www.ibm.com',
class_='class',
remarks='remarks',
)
assert prop
# missing name
try:
prop = csv_to_oscal_cd.row_property_builder(
row=0,
name=None,
value='value',
ns='https://www.ibm.com',
class_='class',
remarks='remarks',
)
raise AssertionError('missing name OK?')
except Exception:
assert prop
# missing value
try:
prop = csv_to_oscal_cd.row_property_builder(
row=0,
name='name',
value=None,
ns='https://www.ibm.com',
class_='class',
remarks='remarks',
)
raise AssertionError('missing value OK?')
except Exception:
assert prop
# invalid ns
try:
prop = csv_to_oscal_cd.row_property_builder(
row=0,
name='name',
value='value',
ns='foobar',
class_='class',
remarks='remarks',
)
raise AssertionError('invalid ns OK?')
except Exception:
assert prop
# invalid class
try:
prop = csv_to_oscal_cd.row_property_builder(
row=0,
name='name',
value='value',
ns='https://www.ibm.com',
class_='\n',
remarks='remarks',
)
raise AssertionError('invalid class OK?')
except Exception:
assert prop
86 changes: 71 additions & 15 deletions trestle/tasks/csv_to_oscal_cd.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,65 @@ def derive_part_id(control_mapping: str) -> str:
return rval


def etype(target: str) -> str:
"""Get etype."""
if target:
return 'invalid'
else:
return 'missing'


def row_property_builder(row: int, name: str, value, ns: str, class_: str, remarks: str) -> Property:
"""Row property builder."""
# name
try:
Property(
name=name,
value='value',
)
except Exception:
text = f'property for row: {row} name: {name} is {etype(name)}'
raise RuntimeError(text)
# value
try:
Property(
name=name,
value=value,
)
except Exception:
text = f'property for row: {row} value: {value} is {etype(value)}'
raise RuntimeError(text)
# ns
try:
Property(
name=name,
value=value,
ns=ns,
)
except Exception:
text = f'property for row: {row} ns: {ns} is {etype(ns)}'
raise RuntimeError(text)
# class
try:
Property(
name=name,
value=value,
class_=class_,
)
except Exception:
text = f'property for row: {row} class: {class_} is {etype(class_)}'
raise RuntimeError(text)
# prop
prop = Property(
name=name,
value=value,
ns=ns,
class_=class_,
remarks=remarks,
)
return prop


class CsvToOscalComponentDefinition(TaskBase):
"""
Task to create OSCAL ComponentDefinition json.
Expand Down Expand Up @@ -911,18 +970,15 @@ def __init__(self, row_number: int, rule_set: str) -> None:
def add_prop(self, name: str, value: str, ns: str, class_: str) -> None:
"""Add prop."""
if value is not None and len(value):
try:
prop = Property(
name=name,
value=value,
ns=ns,
class_=class_,
remarks=self._rule_set,
)
self._props[name] = prop
except Exception:
text = f'Invalid property name: {name} value: {value} remarks: {self._rule_set}'
raise RuntimeError(text)
prop = row_property_builder(
row=self._row_number,
name=name,
value=value,
ns=ns,
class_=class_,
remarks=self._rule_set,
)
self._props[name] = prop

def get_props(self) -> List[Property]:
"""Get props."""
Expand Down Expand Up @@ -1438,8 +1494,6 @@ def __init__(self, csv_path: pathlib.Path) -> None:
self._csv_controls_map = {}
self._csv_profile_list = []
for row_num, row in self.row_generator():
if self._is_no_control(row):
continue
self._check_row_minimum_requirements(row_num, row)
component_title = self.get_row_value(row, f'{COMPONENT_TITLE}')
component_type = self.get_row_value(row, f'{COMPONENT_TYPE}')
Expand All @@ -1454,7 +1508,7 @@ def __init__(self, csv_path: pathlib.Path) -> None:
logger.debug(f'csv-rules: {key} {self._csv_rules_map[key][0]}')
# set parameters, by component
source = self.get_row_value(row, PROFILE_SOURCE)
if source not in self._csv_profile_list:
if source and source not in self._csv_profile_list:
self._csv_profile_list.append(source)
description = self.get_row_value(row, PROFILE_DESCRIPTION)
param_id = self.get_row_value(row, PARAMETER_ID)
Expand Down Expand Up @@ -1489,6 +1543,8 @@ def row_generator(self) -> Generator[Union[int, Iterator[List[str]]], None, None
index += 1
if index < 3:
continue
if self._is_no_control(row):
continue
logger.debug(f'row_gen: {index} {row}')
yield index, row

Expand Down
Loading