mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
feat(prowler_threatscore): add a level for accordion in dashboard (#7739)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
@@ -2569,6 +2569,356 @@ def get_section_containers_3_levels(data, section_1, section_2, section_3):
|
|||||||
return html.Div(section_containers, className="compliance-data-layout")
|
return html.Div(section_containers, className="compliance-data-layout")
|
||||||
|
|
||||||
|
|
||||||
|
def get_section_containers_threatscore(data, section_1, section_2, section_3):
|
||||||
|
data["STATUS"] = data["STATUS"].apply(map_status_to_icon)
|
||||||
|
findings_counts_marco = (
|
||||||
|
data.groupby([section_1, "STATUS"]).size().unstack(fill_value=0)
|
||||||
|
)
|
||||||
|
section_containers = []
|
||||||
|
data[section_1] = data[section_1].astype(str)
|
||||||
|
data[section_2] = data[section_2].astype(str)
|
||||||
|
data[section_3] = data[section_3].astype(str)
|
||||||
|
|
||||||
|
data.sort_values(
|
||||||
|
by=section_3,
|
||||||
|
key=lambda x: x.map(extract_numeric_values),
|
||||||
|
ascending=True,
|
||||||
|
inplace=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
for marco in data[section_1].unique():
|
||||||
|
success_marco = findings_counts_marco.loc[marco].get(pass_emoji, 0)
|
||||||
|
failed_marco = findings_counts_marco.loc[marco].get(fail_emoji, 0)
|
||||||
|
|
||||||
|
fig_name = go.Figure(
|
||||||
|
[
|
||||||
|
go.Bar(
|
||||||
|
name="Failed",
|
||||||
|
x=[failed_marco],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#e77676"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
go.Bar(
|
||||||
|
name="Success",
|
||||||
|
x=[success_marco],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#45cc6e"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
fig_name.update_layout(
|
||||||
|
barmode="stack",
|
||||||
|
margin=dict(l=10, r=10, t=10, b=10),
|
||||||
|
paper_bgcolor="rgba(0,0,0,0)",
|
||||||
|
plot_bgcolor="rgba(0,0,0,0)",
|
||||||
|
showlegend=False,
|
||||||
|
width=350,
|
||||||
|
height=30,
|
||||||
|
xaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
annotations=[
|
||||||
|
dict(
|
||||||
|
x=success_marco + failed_marco,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(success_marco),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#45cc6e", size=14),
|
||||||
|
xanchor="left",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
x=0,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(failed_marco),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#e77676", size=14),
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
fig_name.add_annotation(
|
||||||
|
x=failed_marco,
|
||||||
|
y=0.3,
|
||||||
|
text="|",
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(size=20),
|
||||||
|
xanchor="center",
|
||||||
|
yanchor="middle",
|
||||||
|
)
|
||||||
|
|
||||||
|
graph_div = html.Div(
|
||||||
|
dcc.Graph(
|
||||||
|
figure=fig_name, config={"staticPlot": True}, className="info-bar"
|
||||||
|
),
|
||||||
|
className="graph-section",
|
||||||
|
)
|
||||||
|
direct_internal_items = []
|
||||||
|
|
||||||
|
for categoria in data[data[section_1] == marco][section_2].unique():
|
||||||
|
specific_data = data[
|
||||||
|
(data[section_1] == marco) & (data[section_2] == categoria)
|
||||||
|
]
|
||||||
|
findings_counts_categoria = (
|
||||||
|
specific_data.groupby([section_2, "STATUS"])
|
||||||
|
.size()
|
||||||
|
.unstack(fill_value=0)
|
||||||
|
)
|
||||||
|
success_categoria = findings_counts_categoria.loc[categoria].get(
|
||||||
|
pass_emoji, 0
|
||||||
|
)
|
||||||
|
failed_categoria = findings_counts_categoria.loc[categoria].get(
|
||||||
|
fail_emoji, 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fig_section = go.Figure(
|
||||||
|
[
|
||||||
|
go.Bar(
|
||||||
|
name="Failed",
|
||||||
|
x=[failed_categoria],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#e77676"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
go.Bar(
|
||||||
|
name="Success",
|
||||||
|
x=[success_categoria],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#45cc6e"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
fig_section.update_layout(
|
||||||
|
barmode="stack",
|
||||||
|
margin=dict(l=10, r=10, t=10, b=10),
|
||||||
|
paper_bgcolor="rgba(0,0,0,0)",
|
||||||
|
plot_bgcolor="rgba(0,0,0,0)",
|
||||||
|
showlegend=False,
|
||||||
|
width=350,
|
||||||
|
height=30,
|
||||||
|
xaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
annotations=[
|
||||||
|
dict(
|
||||||
|
x=success_categoria + failed_categoria,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(success_categoria),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#45cc6e", size=14),
|
||||||
|
xanchor="left",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
x=0,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(failed_categoria),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#e77676", size=14),
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
fig_section.add_annotation(
|
||||||
|
x=failed_categoria,
|
||||||
|
y=0.3,
|
||||||
|
text="|",
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(size=20),
|
||||||
|
xanchor="center",
|
||||||
|
yanchor="middle",
|
||||||
|
)
|
||||||
|
|
||||||
|
graph_div_section = html.Div(
|
||||||
|
dcc.Graph(
|
||||||
|
figure=fig_section,
|
||||||
|
config={"staticPlot": True},
|
||||||
|
className="info-bar-child",
|
||||||
|
),
|
||||||
|
className="graph-section-req",
|
||||||
|
)
|
||||||
|
direct_internal_items_idgrupocontrol = []
|
||||||
|
|
||||||
|
for idgrupocontrol in specific_data[section_3].unique():
|
||||||
|
specific_data2 = specific_data[
|
||||||
|
specific_data[section_3] == idgrupocontrol
|
||||||
|
]
|
||||||
|
findings_counts_idgrupocontrol = (
|
||||||
|
specific_data2.groupby([section_3, "STATUS"])
|
||||||
|
.size()
|
||||||
|
.unstack(fill_value=0)
|
||||||
|
)
|
||||||
|
success_idgrupocontrol = findings_counts_idgrupocontrol.loc[
|
||||||
|
idgrupocontrol
|
||||||
|
].get(pass_emoji, 0)
|
||||||
|
failed_idgrupocontrol = findings_counts_idgrupocontrol.loc[
|
||||||
|
idgrupocontrol
|
||||||
|
].get(fail_emoji, 0)
|
||||||
|
|
||||||
|
fig_idgrupocontrol = go.Figure(
|
||||||
|
[
|
||||||
|
go.Bar(
|
||||||
|
name="Failed",
|
||||||
|
x=[failed_idgrupocontrol],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#e77676"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
go.Bar(
|
||||||
|
name="Success",
|
||||||
|
x=[success_idgrupocontrol],
|
||||||
|
y=[""],
|
||||||
|
orientation="h",
|
||||||
|
marker=dict(color="#45cc6e"),
|
||||||
|
width=[0.8],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
fig_idgrupocontrol.update_layout(
|
||||||
|
barmode="stack",
|
||||||
|
margin=dict(l=10, r=10, t=10, b=10),
|
||||||
|
paper_bgcolor="rgba(0,0,0,0)",
|
||||||
|
plot_bgcolor="rgba(0,0,0,0)",
|
||||||
|
showlegend=False,
|
||||||
|
width=350,
|
||||||
|
height=30,
|
||||||
|
xaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False),
|
||||||
|
annotations=[
|
||||||
|
dict(
|
||||||
|
x=success_idgrupocontrol + failed_idgrupocontrol,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(success_idgrupocontrol),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#45cc6e", size=14),
|
||||||
|
xanchor="left",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
x=0,
|
||||||
|
y=0,
|
||||||
|
xref="x",
|
||||||
|
yref="y",
|
||||||
|
text=str(failed_idgrupocontrol),
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(color="#e77676", size=14),
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="middle",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
fig_idgrupocontrol.add_annotation(
|
||||||
|
x=failed_idgrupocontrol,
|
||||||
|
y=0.3,
|
||||||
|
text="|",
|
||||||
|
showarrow=False,
|
||||||
|
font=dict(size=20),
|
||||||
|
xanchor="center",
|
||||||
|
yanchor="middle",
|
||||||
|
)
|
||||||
|
|
||||||
|
graph_div_idgrupocontrol = html.Div(
|
||||||
|
dcc.Graph(
|
||||||
|
figure=fig_idgrupocontrol,
|
||||||
|
config={"staticPlot": True},
|
||||||
|
className="info-bar-child",
|
||||||
|
),
|
||||||
|
className="graph-section-req",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_table = dash_table.DataTable(
|
||||||
|
data=specific_data2.to_dict("records"),
|
||||||
|
columns=[
|
||||||
|
{"name": i, "id": i}
|
||||||
|
for i in [
|
||||||
|
"CHECKID",
|
||||||
|
"STATUS",
|
||||||
|
"REGION",
|
||||||
|
"ACCOUNTID",
|
||||||
|
"RESOURCEID",
|
||||||
|
]
|
||||||
|
],
|
||||||
|
style_table={"overflowX": "auto"},
|
||||||
|
style_as_list_view=True,
|
||||||
|
style_cell={"textAlign": "left", "padding": "5px"},
|
||||||
|
)
|
||||||
|
|
||||||
|
title_internal = f"{idgrupocontrol} - {specific_data2['REQUIREMENTS_DESCRIPTION'].iloc[0]}"
|
||||||
|
|
||||||
|
# Cut the title if it's too long
|
||||||
|
title_internal = (
|
||||||
|
title_internal[:130] + " ..."
|
||||||
|
if len(title_internal) > 130
|
||||||
|
else title_internal
|
||||||
|
)
|
||||||
|
|
||||||
|
internal_accordion_item_2 = dbc.AccordionItem(
|
||||||
|
title=title_internal,
|
||||||
|
children=[
|
||||||
|
graph_div_idgrupocontrol,
|
||||||
|
html.Div([data_table], className="inner-accordion-content"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
direct_internal_items_idgrupocontrol.append(
|
||||||
|
html.Div(
|
||||||
|
[
|
||||||
|
graph_div_idgrupocontrol,
|
||||||
|
dbc.Accordion(
|
||||||
|
[internal_accordion_item_2],
|
||||||
|
start_collapsed=True,
|
||||||
|
flush=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
className="accordion-inner--child",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
internal_accordion_item = dbc.AccordionItem(
|
||||||
|
title=categoria,
|
||||||
|
children=direct_internal_items_idgrupocontrol,
|
||||||
|
)
|
||||||
|
internal_section_container = html.Div(
|
||||||
|
[
|
||||||
|
graph_div_section,
|
||||||
|
dbc.Accordion(
|
||||||
|
[internal_accordion_item], start_collapsed=True, flush=True
|
||||||
|
),
|
||||||
|
],
|
||||||
|
className="accordion-inner--child",
|
||||||
|
)
|
||||||
|
direct_internal_items.append(internal_section_container)
|
||||||
|
|
||||||
|
accordion_item = dbc.AccordionItem(title=marco, children=direct_internal_items)
|
||||||
|
section_container = html.Div(
|
||||||
|
[
|
||||||
|
graph_div,
|
||||||
|
dbc.Accordion([accordion_item], start_collapsed=True, flush=True),
|
||||||
|
],
|
||||||
|
className="accordion-inner",
|
||||||
|
)
|
||||||
|
section_containers.append(section_container)
|
||||||
|
|
||||||
|
return html.Div(section_containers, className="compliance-data-layout")
|
||||||
|
|
||||||
|
|
||||||
# This function extracts and compares up to two numeric values, ensuring correct sorting for version-like strings.
|
# This function extracts and compares up to two numeric values, ensuring correct sorting for version-like strings.
|
||||||
def extract_numeric_values(value):
|
def extract_numeric_values(value):
|
||||||
numbers = re.findall(r"\d+", str(value))
|
numbers = re.findall(r"\d+", str(value))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from dashboard.common_methods import get_section_containers_cis
|
from dashboard.common_methods import get_section_containers_threatscore
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ def get_table(data):
|
|||||||
"REQUIREMENTS_ID",
|
"REQUIREMENTS_ID",
|
||||||
"REQUIREMENTS_DESCRIPTION",
|
"REQUIREMENTS_DESCRIPTION",
|
||||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
"CHECKID",
|
"CHECKID",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
"REGION",
|
"REGION",
|
||||||
@@ -19,6 +20,9 @@ def get_table(data):
|
|||||||
]
|
]
|
||||||
].copy()
|
].copy()
|
||||||
|
|
||||||
return get_section_containers_cis(
|
return get_section_containers_threatscore(
|
||||||
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
aux,
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
|
"REQUIREMENTS_ID",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from dashboard.common_methods import get_section_containers_cis
|
from dashboard.common_methods import get_section_containers_threatscore
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ def get_table(data):
|
|||||||
"REQUIREMENTS_ID",
|
"REQUIREMENTS_ID",
|
||||||
"REQUIREMENTS_DESCRIPTION",
|
"REQUIREMENTS_DESCRIPTION",
|
||||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
"CHECKID",
|
"CHECKID",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
"REGION",
|
"REGION",
|
||||||
@@ -19,6 +20,9 @@ def get_table(data):
|
|||||||
]
|
]
|
||||||
].copy()
|
].copy()
|
||||||
|
|
||||||
return get_section_containers_cis(
|
return get_section_containers_threatscore(
|
||||||
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
aux,
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
|
"REQUIREMENTS_ID",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from dashboard.common_methods import get_section_containers_cis
|
from dashboard.common_methods import get_section_containers_threatscore
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ def get_table(data):
|
|||||||
"REQUIREMENTS_ID",
|
"REQUIREMENTS_ID",
|
||||||
"REQUIREMENTS_DESCRIPTION",
|
"REQUIREMENTS_DESCRIPTION",
|
||||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
"CHECKID",
|
"CHECKID",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
"REGION",
|
"REGION",
|
||||||
@@ -19,6 +20,9 @@ def get_table(data):
|
|||||||
]
|
]
|
||||||
].copy()
|
].copy()
|
||||||
|
|
||||||
return get_section_containers_cis(
|
return get_section_containers_threatscore(
|
||||||
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
aux,
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
|
"REQUIREMENTS_ID",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from dashboard.common_methods import get_section_containers_cis
|
from dashboard.common_methods import get_section_containers_threatscore
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ def get_table(data):
|
|||||||
"REQUIREMENTS_ID",
|
"REQUIREMENTS_ID",
|
||||||
"REQUIREMENTS_DESCRIPTION",
|
"REQUIREMENTS_DESCRIPTION",
|
||||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
"CHECKID",
|
"CHECKID",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
"REGION",
|
"REGION",
|
||||||
@@ -19,6 +20,9 @@ def get_table(data):
|
|||||||
]
|
]
|
||||||
].copy()
|
].copy()
|
||||||
|
|
||||||
return get_section_containers_cis(
|
return get_section_containers_threatscore(
|
||||||
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
aux,
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||||
|
"REQUIREMENTS_ATTRIBUTES_SUBSECTION",
|
||||||
|
"REQUIREMENTS_ID",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
|||||||
### Added
|
### Added
|
||||||
- Add CIS 1.11 compliance framework for Kubernetes. [(#7790)](https://github.com/prowler-cloud/prowler/pull/7790)
|
- Add CIS 1.11 compliance framework for Kubernetes. [(#7790)](https://github.com/prowler-cloud/prowler/pull/7790)
|
||||||
- Support `HTTPS_PROXY` and `K8S_SKIP_TLS_VERIFY` in Kubernetes. [(#7720)](https://github.com/prowler-cloud/prowler/pull/7720)
|
- Support `HTTPS_PROXY` and `K8S_SKIP_TLS_VERIFY` in Kubernetes. [(#7720)](https://github.com/prowler-cloud/prowler/pull/7720)
|
||||||
- Add new check `entra_users_mfa_capable`. [(#7734)](https://github.com/prowler-cloud/prowler/pull/7734)
|
- Add new check `entra_users_mfa_capable` for M365 provider. [(#7734)](https://github.com/prowler-cloud/prowler/pull/7734)
|
||||||
- Add new check `admincenter_organization_customer_lockbox_enabled`. [(#7732)](https://github.com/prowler-cloud/prowler/pull/7732)
|
- Add new check `admincenter_organization_customer_lockbox_enabled` for M365 provider. [(#7732)](https://github.com/prowler-cloud/prowler/pull/7732)
|
||||||
- Add new check `admincenter_external_calendar_sharing_disabled`. [(#7733)](https://github.com/prowler-cloud/prowler/pull/7733)
|
- Add new check `admincenter_external_calendar_sharing_disabled` for M365 provider. [(#7733)](https://github.com/prowler-cloud/prowler/pull/7733)
|
||||||
|
- Add a level for Prowler ThreatScore in the accordion in Dashboard. [(#7739)](https://github.com/prowler-cloud/prowler/pull/7739)
|
||||||
- Add CIS 4.0 compliance framework for GCP. [(7785)](https://github.com/prowler-cloud/prowler/pull/7785)
|
- Add CIS 4.0 compliance framework for GCP. [(7785)](https://github.com/prowler-cloud/prowler/pull/7785)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
Reference in New Issue
Block a user