Compare commits

...

1 Commits

Author SHA1 Message Date
pedrooot
989c270ad1 feat(threatscore): improve the way of scoring from the CLI 2025-08-21 11:43:22 +02:00

View File

@@ -2,6 +2,7 @@ from colorama import Fore, Style
from tabulate import tabulate
from prowler.config.config import orange_color
from prowler.lib.check.compliance_models import Compliance
def get_prowler_threatscore_table(
@@ -25,7 +26,10 @@ def get_prowler_threatscore_table(
pillars = {}
score_per_pillar = {}
max_score_per_pillar = {}
counted_findings = []
counted_findings_per_pillar = {}
generic_score = 0
generic_max_score = 0
generic_counted_findings = []
for index, finding in enumerate(findings):
check = bulk_checks_metadata[finding.check_metadata.CheckID]
check_compliances = check.Compliance
@@ -33,18 +37,24 @@ def get_prowler_threatscore_table(
if compliance.Framework == "ProwlerThreatScore":
for requirement in compliance.Requirements:
for attribute in requirement.Attributes:
# Score per pillar logic
pillar = attribute.Section
if not any(
[
pillar in score_per_pillar.keys(),
pillar in max_score_per_pillar.keys(),
pillar in counted_findings_per_pillar.keys(),
]
):
score_per_pillar[pillar] = 0
max_score_per_pillar[pillar] = 0
counted_findings_per_pillar[pillar] = []
if index not in counted_findings:
if (
index not in counted_findings_per_pillar.get(pillar, [])
and not finding.muted
):
if finding.status == "PASS":
score_per_pillar[pillar] += (
attribute.LevelOfRisk * attribute.Weight
@@ -52,7 +62,7 @@ def get_prowler_threatscore_table(
max_score_per_pillar[pillar] += (
attribute.LevelOfRisk * attribute.Weight
)
counted_findings.append(index)
counted_findings_per_pillar[pillar].append(index)
if pillar not in pillars:
pillars[pillar] = {"FAIL": 0, "PASS": 0, "Muted": 0}
@@ -69,6 +79,17 @@ def get_prowler_threatscore_table(
pass_count.append(index)
pillars[pillar]["PASS"] += 1
# Generic score logic
if index not in generic_counted_findings and not finding.muted:
if finding.status == "PASS":
generic_score += (
attribute.LevelOfRisk * attribute.Weight
)
generic_max_score += (
attribute.LevelOfRisk * attribute.Weight
)
generic_counted_findings.append(index)
pillars = dict(sorted(pillars.items()))
for pillar in pillars:
pillar_table["Provider"].append(compliance.Provider)
@@ -88,6 +109,27 @@ def get_prowler_threatscore_table(
f"{orange_color}{pillars[pillar]['Muted']}{Style.RESET_ALL}"
)
# Add pillars with no findings to the table with Status: PASS and Score: 100%
provider_name = compliance_framework.split("_")[-1]
bulk_compliance_frameworks = Compliance.get_bulk(provider_name)
unique_sections = set()
for compliance_name, compliance in bulk_compliance_frameworks.items():
if compliance_name.startswith(f"prowler_threatscore_{provider_name}"):
for requirement in compliance.Requirements:
for attribute in requirement.Attributes:
unique_sections.add(attribute.Section)
for section in unique_sections:
if section not in pillars:
pillar_table["Provider"].append(provider_name.capitalize())
pillar_table["Pillar"].append(section)
pillar_table["Score"].append(
f"{Style.BRIGHT}{Fore.GREEN}100.00%{Style.RESET_ALL}"
)
pillar_table["Status"].append(f"{Fore.GREEN}PASS(0){Style.RESET_ALL}")
pillar_table["Muted"].append(f"{orange_color}0{Style.RESET_ALL}")
if (
len(fail_count) + len(pass_count) + len(muted_count) > 1
): # If there are no resources, don't print the compliance table
@@ -104,9 +146,12 @@ def get_prowler_threatscore_table(
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
if not compliance_overview:
print(
f"\n{Style.BRIGHT}Overall ThreatScore: {generic_score / generic_max_score * 100:.2f}%{Style.RESET_ALL}"
)
if len(fail_count) > 0 and len(pillar_table["Pillar"]) > 0:
print(
f"\nFramework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:"
f"Framework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:"
)
print(