Files
prowler/tests/lib/outputs/compliance/config_status_renderer_coverage_test.py

72 lines
2.7 KiB
Python

"""Guard that every dedicated compliance renderer applies the config-status rule.
Declaring ``ConfigRequirements`` in a framework JSON is inert unless the renderer
that builds its output actually evaluates them. A requirement whose configurable
checks ran with a config too loose to trust must be forced to FAIL; that override
lives in ``prowler.lib.check.compliance_config_eval`` and every renderer that
emits a finding's status (CSV transform, CLI table, OCSF) must route through it.
This test statically asserts the invariant: any renderer that reads a finding's
raw ``status`` must also reference one of the config-status helpers. It mirrors
the manual audit that caught ``okta_idaas_stig`` shipping ConfigRequirements that
its CSV and table renderers never applied, so the gap cannot silently reopen.
"""
import pathlib
import pytest
_REPO_ROOT = pathlib.Path(__file__).resolve().parents[4]
_RENDERER_DIR = _REPO_ROOT / "prowler" / "lib" / "outputs" / "compliance"
# Base/dispatch modules that orchestrate renderers but never emit a status row.
_EXCLUDED_BASENAMES = {
"__init__.py",
"models.py",
"compliance.py",
"compliance_check.py",
"compliance_output.py",
}
# Any one of these, present in the source, means the renderer wires the override.
_CONFIG_STATUS_HELPERS = (
"apply_config_status",
"build_requirement_config_status",
"resolve_requirement_config_status",
"get_effective_status",
)
# A renderer builds its output from the finding's raw status via one of these.
_RAW_STATUS_MARKERS = ("finding.status", "finding.status_extended")
def _renderer_sources():
for path in sorted(_RENDERER_DIR.glob("**/*.py")):
if path.name in _EXCLUDED_BASENAMES or "__pycache__" in path.parts:
continue
yield path
@pytest.mark.parametrize(
"renderer_path",
[
pytest.param(p, id=str(p.relative_to(_RENDERER_DIR)))
for p in _renderer_sources()
],
)
def test_renderer_emitting_status_applies_config_status(renderer_path):
source = renderer_path.read_text(encoding="utf-8")
uses_raw_status = any(marker in source for marker in _RAW_STATUS_MARKERS)
if not uses_raw_status:
pytest.skip("renderer does not emit a finding's raw status")
applies_config_status = any(helper in source for helper in _CONFIG_STATUS_HELPERS)
assert applies_config_status, (
f"{renderer_path.relative_to(_REPO_ROOT)} emits a finding's raw status but "
"never applies the config-status override. Route it through "
"apply_config_status / build_requirement_config_status (CSV/OCSF) or "
"resolve_requirement_config_status / get_effective_status (CLI table), "
"otherwise its ConfigRequirements are silently ignored."
)