Files
prowler/tests/lib/outputs/compliance/ccc/ccc_table_test.py

131 lines
4.4 KiB
Python

import re
from types import SimpleNamespace
from prowler.lib.outputs.compliance.ccc.ccc import get_ccc_table
def _make_finding(check_id, status="PASS", muted=False):
return SimpleNamespace(
check_metadata=SimpleNamespace(CheckID=check_id),
status=status,
muted=muted,
)
def _make_compliance(provider, sections, framework="CCC"):
"""Build a per-check compliance covering the given sections."""
return SimpleNamespace(
Framework=framework,
Provider=provider,
Requirements=[
SimpleNamespace(Attributes=[SimpleNamespace(Section=section)])
for section in sections
],
)
class TestCCCTable:
"""Test cases verifying multi-section counting and provider-column attribution for the compliance table."""
def test_multi_section_fail_not_undercounted(self, capsys, tmp_path):
"""A single FAIL check mapped to several sections must show FAIL(1) in
every section, not just the first one seen."""
bulk_metadata = {
"check_a": SimpleNamespace(
Compliance=[_make_compliance("aws", ["IAM", "Logging"])]
),
"check_b": SimpleNamespace(Compliance=[_make_compliance("aws", ["IAM"])]),
}
findings = [
_make_finding("check_a", "FAIL"),
_make_finding("check_b", "PASS"),
]
get_ccc_table(
findings,
bulk_metadata,
"ccc_aws",
"output",
str(tmp_path),
False,
)
captured = capsys.readouterr()
# Both IAM and Logging must report FAIL(1); before the fix Logging was
# undercounted because the per-section count was gated by the global
# dedup list.
assert captured.out.count("FAIL(1)") == 2
def test_multi_section_muted_not_undercounted(self, capsys, tmp_path):
"""A single MUTED check mapped to several sections must increase the
per-section Muted count in every section, not only the first one."""
bulk_metadata = {
"check_a": SimpleNamespace(
Compliance=[_make_compliance("aws", ["IAM", "Logging"])]
),
"check_b": SimpleNamespace(Compliance=[_make_compliance("aws", ["IAM"])]),
}
findings = [
_make_finding("check_a", "FAIL", muted=True),
# A real FAIL is needed so the results table is rendered at all.
_make_finding("check_b", "FAIL"),
]
get_ccc_table(
findings,
bulk_metadata,
"ccc_aws",
"output",
str(tmp_path),
False,
)
captured = capsys.readouterr()
plain = re.sub(r"\x1b\[[0-9;]*m", "", captured.out)
# The muted check belongs to both IAM and Logging, so the Muted column
# must read 1 in both rows.
muted_cells = re.findall(r"│\s*1\s*│\s*$", plain, flags=re.MULTILINE)
assert len(muted_cells) == 2
def test_provider_column_not_leaked_from_other_framework(self, capsys, tmp_path):
"""The Provider column must come from the matched CCC compliance, never
from a different framework that happens to be the last entry in the
check's compliance list."""
bulk_metadata = {
"check_a": SimpleNamespace(
Compliance=[
_make_compliance("aws", ["IAM"]),
_make_compliance(
"leaked_provider", ["Other"], framework="OtherFramework"
),
]
),
"check_b": SimpleNamespace(
Compliance=[
_make_compliance("aws", ["IAM"]),
_make_compliance(
"leaked_provider", ["Other"], framework="OtherFramework"
),
]
),
}
findings = [
_make_finding("check_a", "FAIL"),
_make_finding("check_b", "PASS"),
]
get_ccc_table(
findings,
bulk_metadata,
"ccc_aws",
"output",
str(tmp_path),
False,
)
captured = capsys.readouterr()
assert "aws" in captured.out
# The provider of the unrelated trailing framework must NOT leak into
# the rendered table.
assert "leaked_provider" not in captured.out