Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(stats): Use Finding instead of Check_Report #7053

Merged
merged 5 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions prowler/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@
print(f"{Style.BRIGHT}{Fore.GREEN}\nNo findings to fix!{Style.RESET_ALL}\n")
sys.exit()

# Outputs
# TODO: this part is needed since the checks generates a Check_Report_XXX and the output uses Finding
# This will be refactored for the outputs generate directly the Finding
finding_outputs = []
for finding in findings:
try:
finding_outputs.append(

Check warning on line 314 in prowler/__main__.py

View check run for this annotation

Codecov / codecov/patch

prowler/__main__.py#L311-L314

Added lines #L311 - L314 were not covered by tests
Finding.generate_output(global_provider, finding, output_options)
)
except Exception:
continue

Check warning on line 318 in prowler/__main__.py

View check run for this annotation

Codecov / codecov/patch

prowler/__main__.py#L317-L318

Added lines #L317 - L318 were not covered by tests

# Extract findings stats
stats = extract_findings_statistics(findings)

Expand All @@ -329,18 +341,6 @@
)
sys.exit(1)

# Outputs
# TODO: this part is needed since the checks generates a Check_Report_XXX and the output uses Finding
# This will be refactored for the outputs generate directly the Finding
finding_outputs = []
for finding in findings:
try:
finding_outputs.append(
Finding.generate_output(global_provider, finding, output_options)
)
except Exception:
continue

generated_outputs = {"regular": [], "compliance": []}

if args.output_formats:
Expand Down
23 changes: 12 additions & 11 deletions prowler/lib/outputs/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from prowler.config.config import orange_color
from prowler.lib.logger import logger
from prowler.lib.outputs.finding import Finding


def stdout_report(finding, color, verbose, status, fix):
Expand Down Expand Up @@ -89,7 +90,7 @@ def set_report_color(status: str, muted: bool = False) -> str:
return color


def extract_findings_statistics(findings: list) -> dict:
def extract_findings_statistics(findings: list[Finding]) -> dict:
"""
extract_findings_statistics takes a list of findings and returns the following dict with the aggregated statistics
{
Expand Down Expand Up @@ -123,31 +124,31 @@ def extract_findings_statistics(findings: list) -> dict:
low_severity_fail = 0

for finding in findings:
# Save the resource_id
resources.add(finding.resource_id)
# Save the resource_uid
resources.add(finding.resource_uid)

if finding.status == "PASS":
if finding.check_metadata.Severity == "critical":
if finding.metadata.Severity == "critical":
critical_severity_pass += 1
if finding.check_metadata.Severity == "high":
if finding.metadata.Severity == "high":
high_severity_pass += 1
if finding.check_metadata.Severity == "medium":
if finding.metadata.Severity == "medium":
medium_severity_pass += 1
if finding.check_metadata.Severity == "low":
if finding.metadata.Severity == "low":
low_severity_pass += 1
total_pass += 1
findings_count += 1
if finding.muted is True:
muted_pass += 1

if finding.status == "FAIL":
if finding.check_metadata.Severity == "critical":
if finding.metadata.Severity == "critical":
critical_severity_fail += 1
if finding.check_metadata.Severity == "high":
if finding.metadata.Severity == "high":
high_severity_fail += 1
if finding.check_metadata.Severity == "medium":
if finding.metadata.Severity == "medium":
medium_severity_fail += 1
if finding.check_metadata.Severity == "low":
if finding.metadata.Severity == "low":
low_severity_fail += 1
total_fail += 1
findings_count += 1
Expand Down
62 changes: 31 additions & 31 deletions tests/lib/outputs/outputs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,15 @@ def test_parse_json_tags(self):
assert parse_json_tags([{}]) == {}
assert parse_json_tags(None) == {}


class TestExtractFindingStats:
def test_extract_findings_statistics_different_resources(self):
finding_1 = mock.MagicMock()
finding_1.status = "PASS"
finding_1.resource_id = "test_resource_1"
finding_1.resource_uid = "test_resource_1"
finding_2 = mock.MagicMock()
finding_2.status = "FAIL"
finding_2.resource_id = "test_resource_2"
finding_2.resource_uid = "test_resource_2"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand All @@ -266,10 +268,10 @@ def test_extract_findings_statistics_different_resources(self):
def test_extract_findings_statistics_same_resources(self):
finding_1 = mock.MagicMock()
finding_1.status = "PASS"
finding_1.resource_id = "test_resource_1"
finding_1.resource_uid = "test_resource_1"
finding_2 = mock.MagicMock()
finding_2.status = "PASS"
finding_2.resource_id = "test_resource_1"
finding_2.resource_uid = "test_resource_1"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand All @@ -283,10 +285,10 @@ def test_extract_findings_statistics_same_resources(self):
def test_extract_findings_statistics_manual_resources(self):
finding_1 = mock.MagicMock()
finding_1.status = "MANUAL"
finding_1.resource_id = "test_resource_1"
finding_1.resource_uid = "test_resource_1"
finding_2 = mock.MagicMock()
finding_2.status = "PASS"
finding_2.resource_id = "test_resource_1"
finding_2.resource_uid = "test_resource_1"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand Down Expand Up @@ -320,15 +322,15 @@ def test_extract_findings_statistics_all_fail_are_muted(self):
finding_1 = mock.MagicMock()
finding_1.status = "FAIL"
finding_1.muted = True
finding_1.resource_id = "test_resource_1"
finding_1.check_metadata.Severity = "medium"
finding_1.check_metadata.CheckID = "glue_etl_jobs_amazon_s3_encryption_enabled"
finding_1.resource_uid = "test_resource_1"
finding_1.metadata.Severity = "medium"
finding_1.metadata.CheckID = "glue_etl_jobs_amazon_s3_encryption_enabled"
finding_2 = mock.MagicMock()
finding_2.status = "FAIL"
finding_2.muted = True
finding_2.resource_id = "test_resource_2"
finding_2.check_metadata.Severity = "low"
finding_2.check_metadata.CheckID = "lightsail_static_ip_unused"
finding_2.resource_uid = "test_resource_2"
finding_2.metadata.Severity = "low"
finding_2.metadata.CheckID = "lightsail_static_ip_unused"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand All @@ -352,17 +354,15 @@ def test_extract_findings_statistics_all_fail_are_not_muted(self):
finding_1 = mock.MagicMock()
finding_1.status = "FAIL"
finding_1.muted = True
finding_1.resource_id = "test_resource_1"
finding_1.check_metadata.Severity = "critical"
finding_1.check_metadata.CheckID = "rds_instance_certificate_expiration"
finding_1.resource_uid = "test_resource_1"
finding_1.metadata.Severity = "critical"
finding_1.metadata.CheckID = "rds_instance_certificate_expiration"
finding_2 = mock.MagicMock()
finding_2.status = "FAIL"
finding_2.muted = False
finding_2.resource_id = "test_resource_1"
finding_2.check_metadata.Severity = "critical"
finding_2.check_metadata.CheckID = (
"autoscaling_find_secrets_ec2_launch_configuration"
)
finding_2.resource_uid = "test_resource_1"
finding_2.metadata.Severity = "critical"
finding_2.metadata.CheckID = "autoscaling_find_secrets_ec2_launch_configuration"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand All @@ -386,18 +386,16 @@ def test_extract_findings_statistics_all_passes_are_not_muted(self):
finding_1 = mock.MagicMock()
finding_1.status = "PASS"
finding_1.muted = True
finding_1.resource_id = "test_resource_1"
finding_1.check_metadata.Severity = "critical"
finding_1.check_metadata.CheckID = (
"autoscaling_find_secrets_ec2_launch_configuration"
)
finding_1.resource_uid = "test_resource_1"
finding_1.metadata.Severity = "critical"
finding_1.metadata.CheckID = "autoscaling_find_secrets_ec2_launch_configuration"

finding_2 = mock.MagicMock()
finding_2.status = "PASS"
finding_2.muted = False
finding_2.resource_id = "test_resource_1"
finding_2.check_metadata.Severity = "high"
finding_2.check_metadata.CheckID = "acm_certificates_expiration_check"
finding_2.resource_uid = "test_resource_1"
finding_2.metadata.Severity = "high"
finding_2.metadata.CheckID = "acm_certificates_expiration_check"
findings = [finding_1, finding_2]

stats = extract_findings_statistics(findings)
Expand All @@ -420,9 +418,9 @@ def test_extract_findings_statistics_all_passes_are_muted(self):
finding_1 = mock.MagicMock()
finding_1.status = "PASS"
finding_1.muted = True
finding_1.check_metadata.Severity = "critical"
finding_1.check_metadata.CheckID = "rds_instance_certificate_expiration"
finding_1.resource_id = "test_resource_1"
finding_1.metadata.Severity = "critical"
finding_1.metadata.CheckID = "rds_instance_certificate_expiration"
finding_1.resource_uid = "test_resource_1"
findings = [finding_1]

stats = extract_findings_statistics(findings)
Expand All @@ -441,6 +439,8 @@ def test_extract_findings_statistics_all_passes_are_muted(self):
assert stats["total_low_severity_pass"] == 0
assert stats["findings_count"] == 1


class TestReport:
def test_report_with_aws_provider_not_muted_pass(self):
# Mocking check_findings and provider
finding_1 = MagicMock()
Expand Down
Loading