Skip to content

Commit 06f94f8

Browse files
pedroootMrCloudSec
andauthored
feat(compliance): add new Prowler Threat Score Compliance Framework (#7603)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
1 parent b8836c6 commit 06f94f8

26 files changed

+5528
-2
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import warnings
2+
3+
from dashboard.common_methods import get_section_containers_cis
4+
5+
warnings.filterwarnings("ignore")
6+
7+
8+
def get_table(data):
9+
aux = data[
10+
[
11+
"REQUIREMENTS_ID",
12+
"REQUIREMENTS_DESCRIPTION",
13+
"REQUIREMENTS_ATTRIBUTES_SECTION",
14+
"CHECKID",
15+
"STATUS",
16+
"REGION",
17+
"ACCOUNTID",
18+
"RESOURCEID",
19+
]
20+
].copy()
21+
22+
return get_section_containers_cis(
23+
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
24+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import warnings
2+
3+
from dashboard.common_methods import get_section_containers_cis
4+
5+
warnings.filterwarnings("ignore")
6+
7+
8+
def get_table(data):
9+
aux = data[
10+
[
11+
"REQUIREMENTS_ID",
12+
"REQUIREMENTS_DESCRIPTION",
13+
"REQUIREMENTS_ATTRIBUTES_SECTION",
14+
"CHECKID",
15+
"STATUS",
16+
"REGION",
17+
"ACCOUNTID",
18+
"RESOURCEID",
19+
]
20+
].copy()
21+
22+
return get_section_containers_cis(
23+
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
24+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import warnings
2+
3+
from dashboard.common_methods import get_section_containers_cis
4+
5+
warnings.filterwarnings("ignore")
6+
7+
8+
def get_table(data):
9+
aux = data[
10+
[
11+
"REQUIREMENTS_ID",
12+
"REQUIREMENTS_DESCRIPTION",
13+
"REQUIREMENTS_ATTRIBUTES_SECTION",
14+
"CHECKID",
15+
"STATUS",
16+
"REGION",
17+
"ACCOUNTID",
18+
"RESOURCEID",
19+
]
20+
].copy()
21+
22+
return get_section_containers_cis(
23+
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
24+
)

dashboard/pages/compliance.py

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ def load_csv_files(files):
398398
f"dashboard.compliance.{current}"
399399
)
400400
data.drop_duplicates(keep="first", inplace=True)
401+
402+
if "threatscore" in analytics_input:
403+
data = get_threatscore_mean_by_pillar(data)
404+
401405
table = compliance_module.get_table(data)
402406
except ModuleNotFoundError:
403407
table = html.Div(
@@ -430,6 +434,9 @@ def load_csv_files(files):
430434
if "pci" in analytics_input:
431435
pie_2 = get_bar_graph(df, "REQUIREMENTS_ID")
432436
current_filter = "req_id"
437+
elif "threatscore" in analytics_input:
438+
pie_2 = get_table_prowler_threatscore(df)
439+
current_filter = "threatscore"
433440
elif (
434441
"REQUIREMENTS_ATTRIBUTES_SECTION" in df.columns
435442
and not df["REQUIREMENTS_ATTRIBUTES_SECTION"].isnull().values.any()
@@ -488,6 +495,13 @@ def load_csv_files(files):
488495
pie_2, f"Top 5 failed {current_filter} by requirements"
489496
)
490497

498+
if "threatscore" in analytics_input:
499+
security_level_graph = get_graph(
500+
pie_2,
501+
"Pillar Score by requirements (1 = Lowest Risk, 5 = Highest Risk)",
502+
margin_top=0,
503+
)
504+
491505
return (
492506
table_output,
493507
overall_status_result_graph,
@@ -501,7 +515,7 @@ def load_csv_files(files):
501515
)
502516

503517

504-
def get_graph(pie, title):
518+
def get_graph(pie, title, margin_top=7):
505519
return [
506520
html.Span(
507521
title,
@@ -514,7 +528,7 @@ def get_graph(pie, title):
514528
"display": "flex",
515529
"justify-content": "center",
516530
"align-items": "center",
517-
"margin-top": "7%",
531+
"margin-top": f"{margin_top}%",
518532
},
519533
),
520534
]
@@ -618,3 +632,87 @@ def get_table(current_compliance, table):
618632
className="relative flex flex-col bg-white shadow-provider rounded-xl px-4 py-3 flex-wrap w-full",
619633
),
620634
]
635+
636+
637+
def get_threatscore_mean_by_pillar(df):
638+
modified_df = df[df["STATUS"] == "FAIL"]
639+
640+
modified_df["REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"] = pd.to_numeric(
641+
modified_df["REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"], errors="coerce"
642+
)
643+
644+
pillar_means = (
645+
modified_df.groupby("REQUIREMENTS_ATTRIBUTES_SECTION")[
646+
"REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"
647+
]
648+
.mean()
649+
.round(2)
650+
)
651+
652+
output = []
653+
for pillar, mean in pillar_means.items():
654+
output.append(f"{pillar} - [{mean}]")
655+
656+
for value in output:
657+
if value.split(" - ")[0] in df["REQUIREMENTS_ATTRIBUTES_SECTION"].values:
658+
df.loc[
659+
df["REQUIREMENTS_ATTRIBUTES_SECTION"] == value.split(" - ")[0],
660+
"REQUIREMENTS_ATTRIBUTES_SECTION",
661+
] = value
662+
return df
663+
664+
665+
def get_table_prowler_threatscore(df):
666+
df = df[df["STATUS"] == "FAIL"]
667+
668+
# Delete " - " from the column REQUIREMENTS_ATTRIBUTES_SECTION
669+
df["REQUIREMENTS_ATTRIBUTES_SECTION"] = (
670+
df["REQUIREMENTS_ATTRIBUTES_SECTION"].str.split(" - ").str[0]
671+
)
672+
673+
df["REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"] = pd.to_numeric(
674+
df["REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"], errors="coerce"
675+
)
676+
677+
score_df = (
678+
df.groupby("REQUIREMENTS_ATTRIBUTES_SECTION")[
679+
"REQUIREMENTS_ATTRIBUTES_LEVELOFRISK"
680+
]
681+
.mean()
682+
.reset_index()
683+
.rename(
684+
columns={
685+
"REQUIREMENTS_ATTRIBUTES_SECTION": "Pillar",
686+
"REQUIREMENTS_ATTRIBUTES_LEVELOFRISK": "Score",
687+
}
688+
)
689+
)
690+
691+
fig = px.bar(
692+
score_df,
693+
x="Pillar",
694+
y="Score",
695+
color="Score",
696+
color_continuous_scale=[
697+
"#45cc6e",
698+
"#f4d44d",
699+
"#e77676",
700+
], # verde → amarillo → rojo
701+
hover_data={"Score": True, "Pillar": True},
702+
labels={"Score": "Average Risk Score", "Pillar": "Section"},
703+
height=400,
704+
)
705+
706+
fig.update_layout(
707+
xaxis_title="Pillar",
708+
yaxis_title="Level of Risk",
709+
margin=dict(l=20, r=20, t=30, b=20),
710+
plot_bgcolor="rgba(0,0,0,0)",
711+
paper_bgcolor="rgba(0,0,0,0)",
712+
coloraxis_colorbar=dict(title="Risk"),
713+
)
714+
715+
return dcc.Graph(
716+
figure=fig,
717+
style={"height": "25rem", "width": "40rem"},
718+
)

prowler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
3535
- Add new check `teams_meeting_recording_disabled` [(#7607)](https://github.com/prowler-cloud/prowler/pull/7607)
3636
- Add new check `teams_meeting_presenters_restricted` [(#7613)](https://github.com/prowler-cloud/prowler/pull/7613)
3737
- Add new check `teams_meeting_chat_anonymous_users_disabled` [(#7579)](https://github.com/prowler-cloud/prowler/pull/7579)
38+
- Add Prowler Threat Score Compliance Framework [(#7603)](https://github.com/prowler-cloud/prowler/pull/7603)
3839

3940
### Fixed
4041

prowler/__main__.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@
7070
AzureMitreAttack,
7171
)
7272
from prowler.lib.outputs.compliance.mitre_attack.mitre_attack_gcp import GCPMitreAttack
73+
from prowler.lib.outputs.compliance.prowler_threatscore.prowler_threatscore_aws import (
74+
ProwlerThreatScoreAWS,
75+
)
76+
from prowler.lib.outputs.compliance.prowler_threatscore.prowler_threatscore_azure import (
77+
ProwlerThreatScoreAzure,
78+
)
79+
from prowler.lib.outputs.compliance.prowler_threatscore.prowler_threatscore_gcp import (
80+
ProwlerThreatScoreGCP,
81+
)
7382
from prowler.lib.outputs.csv.csv import CSV
7483
from prowler.lib.outputs.finding import Finding
7584
from prowler.lib.outputs.html.html import HTML
@@ -478,6 +487,18 @@ def prowler():
478487
)
479488
generated_outputs["compliance"].append(kisa_ismsp)
480489
kisa_ismsp.batch_write_data_to_file()
490+
elif compliance_name == "prowler_threatscore_aws":
491+
filename = (
492+
f"{output_options.output_directory}/compliance/"
493+
f"{output_options.output_filename}_{compliance_name}.csv"
494+
)
495+
prowler_threatscore = ProwlerThreatScoreAWS(
496+
findings=finding_outputs,
497+
compliance=bulk_compliance_frameworks[compliance_name],
498+
file_path=filename,
499+
)
500+
generated_outputs["compliance"].append(prowler_threatscore)
501+
prowler_threatscore.batch_write_data_to_file()
481502
else:
482503
filename = (
483504
f"{output_options.output_directory}/compliance/"
@@ -545,6 +566,18 @@ def prowler():
545566
)
546567
generated_outputs["compliance"].append(iso27001)
547568
iso27001.batch_write_data_to_file()
569+
elif compliance_name == "prowler_threatscore_azure":
570+
filename = (
571+
f"{output_options.output_directory}/compliance/"
572+
f"{output_options.output_filename}_{compliance_name}.csv"
573+
)
574+
prowler_threatscore = ProwlerThreatScoreAzure(
575+
findings=finding_outputs,
576+
compliance=bulk_compliance_frameworks[compliance_name],
577+
file_path=filename,
578+
)
579+
generated_outputs["compliance"].append(prowler_threatscore)
580+
prowler_threatscore.batch_write_data_to_file()
548581
else:
549582
filename = (
550583
f"{output_options.output_directory}/compliance/"
@@ -612,6 +645,18 @@ def prowler():
612645
)
613646
generated_outputs["compliance"].append(iso27001)
614647
iso27001.batch_write_data_to_file()
648+
elif compliance_name == "prowler_threatscore_gcp":
649+
filename = (
650+
f"{output_options.output_directory}/compliance/"
651+
f"{output_options.output_filename}_{compliance_name}.csv"
652+
)
653+
prowler_threatscore = ProwlerThreatScoreGCP(
654+
findings=finding_outputs,
655+
compliance=bulk_compliance_frameworks[compliance_name],
656+
file_path=filename,
657+
)
658+
generated_outputs["compliance"].append(prowler_threatscore)
659+
prowler_threatscore.batch_write_data_to_file()
615660
else:
616661
filename = (
617662
f"{output_options.output_directory}/compliance/"

0 commit comments

Comments
 (0)