Files
prowler/tests/config/schema/exclusions_test.py

114 lines
4.6 KiB
Python

"""Coverage for the ``excluded_checks`` / ``excluded_services`` fields
added to :class:`prowler.config.schema.base.ProviderConfigBase`.
Because the fields live on the base class, every registered provider
schema exposes them and every provider must therefore share the same
whitespace / uniqueness / non-empty guarantees. These tests lock in that
contract at the base level and at the JSON-Schema level (which the UI
editor consumes via ``ajv``).
"""
import pytest
from pydantic import ValidationError
from prowler.config.scan_config_schema import SCAN_CONFIG_SCHEMA
from prowler.config.schema.aws import AWSProviderConfig
from prowler.config.schema.registry import SCHEMAS
from prowler.config.schema.validator import validate_provider_config
EXCLUSION_FIELDS = ("excluded_checks", "excluded_services")
class Test_JSON_Schema_Exposes_Exclusion_Fields:
@pytest.mark.parametrize("provider", sorted(SCHEMAS))
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_field_shape(self, provider, field):
field_schema = SCAN_CONFIG_SCHEMA["properties"][provider]["properties"][field]
assert field_schema["type"] == "array"
assert field_schema["items"] == {"type": "string", "minLength": 1}
assert field_schema["uniqueItems"] is True
assert field_schema["default"] == []
class Test_Exclusion_Field_Validation:
def _model(self, **kwargs):
return AWSProviderConfig.model_validate(kwargs)
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_empty_string_is_rejected(self, field):
with pytest.raises(ValidationError):
self._model(**{field: [""]})
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_whitespace_only_string_is_rejected(self, field):
with pytest.raises(ValidationError):
self._model(**{field: [" "]})
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_raw_duplicates_are_rejected(self, field):
with pytest.raises(ValidationError):
self._model(**{field: ["s3", "s3"]})
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_normalized_duplicates_are_rejected(self, field):
# After whitespace normalization ``" s3 "`` collapses to ``"s3"``
# and must be caught by the duplicate check.
with pytest.raises(ValidationError):
self._model(**{field: ["s3", " s3 "]})
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_whitespace_is_stripped(self, field):
model = self._model(**{field: [" identifier "]})
assert getattr(model, field) == ["identifier"]
@pytest.mark.parametrize("field", EXCLUSION_FIELDS)
def test_non_string_item_is_rejected(self, field):
with pytest.raises(ValidationError):
self._model(**{field: [123]})
class Test_Exclusion_Defaults_Are_Not_Injected:
"""The strict normalization path uses ``model_dump(exclude_unset=True)``
so pre-existing configs that never set an exclusion field must round-trip
without the default empty list being materialized."""
def test_absent_fields_are_not_injected_by_validator(self):
# The lenient SDK runtime path also uses ``exclude_unset=True`` under
# the hood; asserting on the validator output guards the promise
# against future refactors.
assert validate_provider_config("aws", {}, SCHEMAS["aws"]) == {}
def test_absent_fields_are_not_injected_when_other_keys_are_present(self):
assert validate_provider_config(
"aws",
{"max_ec2_instance_age_in_days": 180},
SCHEMAS["aws"],
) == {"max_ec2_instance_age_in_days": 180}
def test_explicit_empty_list_round_trips(self):
# Explicitly setting ``excluded_checks: []`` is different from
# omitting it — the empty list is user-provided and must be
# preserved by the strict-normalization contract.
assert validate_provider_config(
"aws",
{"excluded_checks": []},
SCHEMAS["aws"],
) == {"excluded_checks": []}
class Test_Extra_Fields_Are_Preserved:
"""``extra="allow"`` must keep plugin-provided keys around so the
ecosystem contract in ``validator_test.py`` still holds after adding
the exclusion fields."""
def test_unknown_keys_are_preserved_alongside_exclusions(self):
out = validate_provider_config(
"aws",
{"excluded_checks": ["s3_bucket_public_access"], "plugin_option": "kept"},
SCHEMAS["aws"],
)
assert out == {
"excluded_checks": ["s3_bucket_public_access"],
"plugin_option": "kept",
}