Compare commits

...

6 Commits

3 changed files with 117 additions and 7 deletions

View File

@@ -7,6 +7,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Added
- Support for AdditionalURLs in outputs [(#8651)](https://github.com/prowler-cloud/prowler/pull/8651)
- Support for markdown metadata fields in Dashboard [(#8667)](https://github.com/prowler-cloud/prowler/pull/8667)
- Equality validation for CheckID, filename and classname [(#8690)](https://github.com/prowler-cloud/prowler/pull/8690)
### Changed
- Update AWS Neptune service metadata to new format [(#8494)](https://github.com/prowler-cloud/prowler/pull/8494)

View File

@@ -8,6 +8,7 @@ from enum import Enum
from typing import Any, Dict, Optional, Set
from pydantic.v1 import BaseModel, Field, ValidationError, validator
from pydantic.v1.error_wrappers import ErrorWrapper
from prowler.config.config import Provider
from prowler.lib.check.compliance_models import Compliance
@@ -436,17 +437,31 @@ class Check(ABC, CheckMetadata):
def __init__(self, **data):
"""Check's init function. Calls the CheckMetadataModel init."""
file_path = os.path.abspath(sys.modules[self.__module__].__file__)[:-3]
# Parse the Check's metadata file
metadata_file = (
os.path.abspath(sys.modules[self.__module__].__file__)[:-3]
+ ".metadata.json"
)
metadata_file = file_path + ".metadata.json"
# Store it to validate them with Pydantic
data = CheckMetadata.parse_file(metadata_file).dict()
# Calls parents init function
super().__init__(**data)
# TODO: verify that the CheckID is the same as the filename and classname
# to mimic the test done at test_<provider>_checks_metadata_is_valid
# Verify names consistency
check_id = self.CheckID
class_name = self.__class__.__name__
file_name = file_path.split(sep="/")[-1]
errors = []
if check_id != class_name:
errors.append(f"CheckID '{check_id}' != class name '{class_name}'")
if check_id != file_name:
errors.append(f"CheckID '{check_id}' != file name '{file_name}'")
if errors:
formatted_errors = [
ErrorWrapper(ValueError(err), loc=("CheckID",)) for err in errors
]
raise ValidationError(formatted_errors, model=CheckMetadata)
def metadata(self) -> dict:
"""Return the JSON representation of the check's metadata"""

View File

@@ -1,9 +1,10 @@
import sys
from unittest import mock
import pytest
from pydantic.v1 import ValidationError
from prowler.lib.check.models import CheckMetadata
from prowler.lib.check.models import Check, CheckMetadata
from tests.lib.check.compliance_check_test import custom_compliance_metadata
mock_metadata = CheckMetadata(
@@ -716,3 +717,96 @@ class TestCheckMetada:
)
# Should contain the validation error we set in the validator
assert "AdditionalURLs must be a list" in str(exc_info.value)
class TestCheck:
@mock.patch("prowler.lib.check.models.CheckMetadata.parse_file")
def test_verify_names_consistency_all_match(self, mock_parse_file):
"""Case where everything matches: CheckID == class_name == file_name"""
mock_parse_file.return_value = mock_metadata.copy(
update={
"CheckID": "accessanalyzer_enabled",
"ServiceName": "accessanalyzer",
}
)
class accessanalyzer_enabled(Check):
def execute(self):
pass
fake_module = mock.Mock()
fake_module.__file__ = "/path/to/accessanalyzer_enabled.py"
sys.modules[accessanalyzer_enabled.__module__] = fake_module
accessanalyzer_enabled()
@mock.patch("prowler.lib.check.models.CheckMetadata.parse_file")
def test_verify_names_consistency_class_mismatch(self, mock_parse_file):
"""CheckID != class name, but matches file_name"""
mock_parse_file.return_value = mock_metadata.copy(
update={
"CheckID": "accessanalyzer_enabled",
"ServiceName": "accessanalyzer",
}
)
class WrongClass(Check):
def execute(self):
pass
fake_module = mock.Mock()
fake_module.__file__ = "/path/to/accessanalyzer_enabled.py"
sys.modules[WrongClass.__module__] = fake_module
with pytest.raises(ValidationError) as excinfo:
WrongClass()
assert "!= class name" in str(excinfo.value)
@mock.patch("prowler.lib.check.models.CheckMetadata.parse_file")
def test_verify_names_consistency_file_mismatch(self, mock_parse_file):
"""CheckID == class name, but != file_name"""
mock_parse_file.return_value = mock_metadata.copy(
update={
"CheckID": "accessanalyzer_enabled",
"ServiceName": "accessanalyzer",
}
)
class accessanalyzer_enabled(Check):
def execute(self):
pass
fake_module = mock.Mock()
fake_module.__file__ = "/path/to/OtherFile.py"
sys.modules[accessanalyzer_enabled.__module__] = fake_module
with pytest.raises(ValidationError) as excinfo:
accessanalyzer_enabled()
assert "!= file name" in str(excinfo.value)
@mock.patch("prowler.lib.check.models.CheckMetadata.parse_file")
def test_verify_names_consistency_both_mismatch(self, mock_parse_file):
"""Neither class name nor file name match the CheckID"""
mock_parse_file.return_value = mock_metadata.copy(
update={
"CheckID": "accessanalyzer_enabled",
"ServiceName": "accessanalyzer",
}
)
class WrongClass(Check):
def execute(self):
pass
fake_module = mock.Mock()
fake_module.__file__ = "/path/to/OtherFile.py"
sys.modules[WrongClass.__module__] = fake_module
with pytest.raises(ValidationError) as excinfo:
WrongClass()
msg = str(excinfo.value)
assert "!= class name" in msg
assert "!= file name" in msg