Skip to content

Commit

Permalink
feat(transformer): add basic validation for TrestleRule with pydantic
Browse files Browse the repository at this point in the history
Signed-off-by: Jennifer Power <[email protected]>
  • Loading branch information
jpower432 committed Sep 30, 2023
1 parent f28c796 commit 49d940b
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 33 deletions.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ compliance-trestle = {git = "https://github.com/IBM/compliance-trestle.git", rev
github3-py = "^4.0.1"
python-gitlab = "^3.15.0"
ruamel-yaml = "^0.17.32"
pydantic = "1.10.12"

[tool.poetry.group.dev.dependencies]
flake8 = "^6.0.0"
Expand Down
17 changes: 17 additions & 0 deletions tests/data/yaml/test_invalid_rule.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
x-trestle-rule-info:
name: example_rule_1
description: My rule description for example rule 1
parameter:
name: prm_1
description: prm_1 description
alternative-values: "invalid"
default-value: true
profile:
description: Simple NIST Profile
href: profiles/simplified_nist_profile/profile.json
include-controls:
- id: ac-2
x-trestle-component-info:
name: Component 1
description: Component 1 description
type: service
22 changes: 19 additions & 3 deletions tests/trestlebot/transformers/test_yaml_to_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
import pytest

from tests.testutils import YAML_TEST_DATA_PATH
from trestlebot.transformers.base_transformer import RulesTransformerException
from trestlebot.transformers.trestle_rule import (
ComponentInfo,
Control,
Parameter,
Profile,
RulesTransformerException,
TrestleRule,
)
from trestlebot.transformers.yaml_to_csv import CSVBuilder, RulesYAMLTransformer
Expand Down Expand Up @@ -123,8 +123,8 @@ def test_rule_transformer() -> None:
assert rule.profile.href == "profiles/simplified_nist_profile/profile.json"


def test_rules_transform_with_invalid_rule() -> None:
"""Test rules transform with invalid rule."""
def test_rules_transform_with_incomplete_rule() -> None:
"""Test rules transform with incomplete rule."""
# Generate test json string
test_string = '{"test_json": "test"}'
transformer = RulesYAMLTransformer()
Expand All @@ -134,3 +134,19 @@ def test_rules_transform_with_invalid_rule() -> None:
match="Missing key in YAML file: 'x-trestle-rule-info'",
):
transformer.transform(test_string)


def test_rules_transform_with_invalid_rule() -> None:
"""Test rules transform with invalid rule."""
# load rule from path and close the file
# get the file info as a string
rule_path = YAML_TEST_DATA_PATH / "test_invalid_rule.yaml"
rule_file = open(rule_path, "r")
rule_file_info = rule_file.read()
rule_file.close()
transformer = RulesYAMLTransformer()

with pytest.raises(
RulesTransformerException, match="Invalid YAML file: 1 validation error .*"
):
transformer.transform(rule_file_info)
2 changes: 1 addition & 1 deletion trestlebot/tasks/rule_transform_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import trestlebot.const as const
from trestlebot.tasks.base_task import TaskBase, TaskException
from trestlebot.transformers.trestle_rule import (
from trestlebot.transformers.base_transformer import (
RulesTransformer,
RulesTransformerException,
)
Expand Down
35 changes: 35 additions & 0 deletions trestlebot/transformers/base_transformer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/python

# Copyright 2023 Red Hat, Inc.
#
# 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
#
# http://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.

"""Base transformer for rules."""

from abc import abstractmethod

from trestle.transforms.transformer_factory import TransformerBase

from trestlebot.transformers.trestle_rule import TrestleRule


class RulesTransformer(TransformerBase):
"""Abstract interface for transformers for rules"""

@abstractmethod
def transform(self, blob: str) -> TrestleRule:
"""Transform rule data."""


class RulesTransformerException(Exception):
"""An error during transformation of a rule"""
33 changes: 7 additions & 26 deletions trestlebot/transformers/trestle_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@
# License for the specific language governing permissions and limitations
# under the License.

"""Trestle Rule Dataclass and base transformer."""
"""Trestle Rule Dataclass."""

from abc import abstractmethod
from dataclasses import dataclass
from typing import Any, Dict, List, Optional

from trestle.transforms.transformer_factory import TransformerBase
from pydantic import BaseModel


@dataclass
class Parameter:
class Parameter(BaseModel):
"""Parameter dataclass."""

name: str
Expand All @@ -33,49 +30,33 @@ class Parameter:
default_value: str


@dataclass
class Control:
class Control(BaseModel):
"""Control dataclass."""

id: str


@dataclass
class Profile:
class Profile(BaseModel):
"""Profile dataclass."""

description: str
href: str
include_controls: List[Control]


@dataclass
class ComponentInfo:
class ComponentInfo(BaseModel):
"""ComponentInfo dataclass."""

name: str
type: str
description: str


@dataclass
class TrestleRule:
class TrestleRule(BaseModel):
"""TrestleRule dataclass."""

name: str
description: str
component: ComponentInfo
parameter: Optional[Parameter]
profile: Profile


class RulesTransformer(TransformerBase):
"""Abstract interface for transformers for rules"""

@abstractmethod
def transform(self, blob: str) -> TrestleRule:
"""Transform rule data."""


class RulesTransformerException(Exception):
"""An error during transformation of a rule"""
9 changes: 7 additions & 2 deletions trestlebot/transformers/yaml_to_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import pathlib
from typing import Any, Dict, List

from pydantic import ValidationError
from ruamel.yaml import YAML
from trestle.common.const import TRESTLE_GENERIC_NS
from trestle.tasks.csv_to_oscal_cd import (
Expand All @@ -40,13 +41,15 @@
)

from trestlebot import const
from trestlebot.transformers.base_transformer import (
RulesTransformer,
RulesTransformerException,
)
from trestlebot.transformers.trestle_rule import (
ComponentInfo,
Control,
Parameter,
Profile,
RulesTransformer,
RulesTransformerException,
TrestleRule,
)

Expand Down Expand Up @@ -111,6 +114,8 @@ def _ingest_yaml(blob: str) -> TrestleRule:

except KeyError as e:
raise RulesTransformerException(f"Missing key in YAML file: {e}")
except ValidationError as e:
raise RulesTransformerException(f"Invalid YAML file: {e}")
except Exception as e:
raise RuntimeError(e)

Expand Down

0 comments on commit 49d940b

Please sign in to comment.