mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
fix(Dashboard): Multiple dashboard fixes (#3654)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
+21
-17
@@ -35,7 +35,7 @@ dashboard = dash.Dash(
|
||||
|
||||
# Logo
|
||||
prowler_logo = html.Img(
|
||||
src="https://prowler.com/logo-dashboard.png", alt="Prowler Logo"
|
||||
src="https://prowler.com/wp-content/uploads/logo-dashboard.png", alt="Prowler Logo"
|
||||
)
|
||||
|
||||
menu_icons = {
|
||||
@@ -119,7 +119,9 @@ dashboard.layout = html.Div(
|
||||
# Placeholder for dynamic navigation bar
|
||||
html.Div(
|
||||
[
|
||||
html.Div(id="navigation-bar", className="bg-prowler-stone-900"),
|
||||
html.Div(
|
||||
id="navigation-bar", className="bg-prowler-stone-900 min-w-36 z-10"
|
||||
),
|
||||
html.Div(
|
||||
[
|
||||
dash.page_container,
|
||||
@@ -149,22 +151,24 @@ def update_nav_bar(pathname):
|
||||
[html.Ul(generate_nav_links(pathname), className="")],
|
||||
className="flex flex-col gap-y-6",
|
||||
),
|
||||
html.A(
|
||||
[
|
||||
html.Span(
|
||||
[
|
||||
html.Img(src="assets/favicon.ico", className="w-5"),
|
||||
"Subscribe to prowler SaaS",
|
||||
],
|
||||
className="flex items-center gap-x-3",
|
||||
),
|
||||
],
|
||||
href="https://prowler.com/",
|
||||
target="_blank",
|
||||
className="block mt-300 px-3 py-3 uppercase text-xs hover:bg-prowler-stone-950 hover:border-r-4 hover:border-solid hover:border-prowler-lime",
|
||||
),
|
||||
html.Nav(
|
||||
[html.Ul(generate_help_menu(), className="")],
|
||||
[
|
||||
html.A(
|
||||
[
|
||||
html.Span(
|
||||
[
|
||||
html.Img(src="assets/favicon.ico", className="w-5"),
|
||||
"Subscribe to prowler SaaS",
|
||||
],
|
||||
className="flex items-center gap-x-3",
|
||||
),
|
||||
],
|
||||
href="https://prowler.com/",
|
||||
target="_blank",
|
||||
className="block p-3 uppercase text-xs hover:bg-prowler-stone-950 hover:border-r-4 hover:border-solid hover:border-prowler-lime",
|
||||
),
|
||||
html.Ul(generate_help_menu(), className=""),
|
||||
],
|
||||
className="flex flex-col gap-y-6 mt-auto",
|
||||
),
|
||||
],
|
||||
|
||||
+16
@@ -660,6 +660,10 @@ video {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.col-span-11 {
|
||||
grid-column: span 11 / span 11;
|
||||
}
|
||||
@@ -729,6 +733,14 @@ video {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.min-w-36 {
|
||||
min-width: 9rem;
|
||||
}
|
||||
|
||||
.min-w-44 {
|
||||
min-width: 11rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
@@ -894,6 +906,10 @@ video {
|
||||
background-image: linear-gradient(127.43deg, #F1F5F8 -177.68%, #636c78 87.35%);
|
||||
}
|
||||
|
||||
.p-3 {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
+52
-43
@@ -61,7 +61,8 @@ def load_csv_files(csv_files):
|
||||
for file in csv_files:
|
||||
df = pd.read_csv(file, sep=";", on_bad_lines="skip")
|
||||
if "CHECK_ID" in df.columns:
|
||||
dfs.append(df.astype(str))
|
||||
if "TIMESTAMP" in df.columns or df["PROVIDER"].unique() == "aws":
|
||||
dfs.append(df.astype(str))
|
||||
# Handle the case where there are no files
|
||||
try:
|
||||
data = pd.concat(dfs, ignore_index=True)
|
||||
@@ -101,35 +102,43 @@ else:
|
||||
data["ASSESSMENT_START_TIME"] = data["ASSESSMENT_START_TIME"].str.replace(
|
||||
"T", " "
|
||||
)
|
||||
# for each row, we are going to take the ASSESMENT_START_TIME if is not null and put it in the TIMESTAMP column
|
||||
data.rename(columns={"ASSESSMENT_START_TIME": "TIMESTAMP_AUX"}, inplace=True)
|
||||
# Unify the columns
|
||||
data["TIMESTAMP"] = data.apply(
|
||||
lambda x: (
|
||||
x["ASSESSMENT_START_TIME"]
|
||||
if pd.isnull(x["TIMESTAMP"])
|
||||
else x["TIMESTAMP"]
|
||||
x["TIMESTAMP_AUX"] if pd.isnull(x["TIMESTAMP"]) else x["TIMESTAMP"]
|
||||
),
|
||||
axis=1,
|
||||
)
|
||||
if "ACCOUNT_ID" in data.columns:
|
||||
data.rename(columns={"ACCOUNT_ID": "ACCOUNT_UID_AUX"}, inplace=True)
|
||||
data["ACCOUNT_UID"] = data.apply(
|
||||
lambda x: (
|
||||
x["ACCOUNT_ID"] if pd.isnull(x["ACCOUNT_UID"]) else x["ACCOUNT_UID"]
|
||||
x["ACCOUNT_UID_AUX"]
|
||||
if pd.isnull(x["ACCOUNT_UID"])
|
||||
else x["ACCOUNT_UID"]
|
||||
),
|
||||
axis=1,
|
||||
)
|
||||
# Rename the column RESOURCE_ID to RESOURCE_UID
|
||||
if "RESOURCE_ID" in data.columns:
|
||||
data.rename(columns={"RESOURCE_ID": "RESOURCE_UID_AUX"}, inplace=True)
|
||||
data["RESOURCE_UID"] = data.apply(
|
||||
lambda x: (
|
||||
x["RESOURCE_ID"] if pd.isnull(x["RESOURCE_UID"]) else x["RESOURCE_UID"]
|
||||
x["RESOURCE_UID_AUX"]
|
||||
if pd.isnull(x["RESOURCE_UID"])
|
||||
else x["RESOURCE_UID"]
|
||||
),
|
||||
axis=1,
|
||||
)
|
||||
# Rename the column "SUBSCRIPTION" to "ACCOUNT_UID"
|
||||
if "SUBSCRIPTION" in data.columns:
|
||||
data.rename(columns={"SUBSCRIPTION": "ACCOUNT_UID_AUX"}, inplace=True)
|
||||
data["ACCOUNT_UID"] = data.apply(
|
||||
lambda x: (
|
||||
x["SUBSCRIPTION"] if pd.isnull(x["ACCOUNT_UID"]) else x["ACCOUNT_UID"]
|
||||
x["ACCOUNT_UID_AUX"]
|
||||
if pd.isnull(x["ACCOUNT_UID"])
|
||||
else x["ACCOUNT_UID"]
|
||||
),
|
||||
axis=1,
|
||||
)
|
||||
@@ -252,8 +261,14 @@ else:
|
||||
Input("cloud-account-filter", "value"),
|
||||
Input("region-filter", "value"),
|
||||
Input("report-date-filter", "value"),
|
||||
Input("download_link", "n_clicks"),
|
||||
)
|
||||
def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
def filter_data(
|
||||
cloud_account_values, region_account_values, assessment_value, n_clicks
|
||||
):
|
||||
# Use n_clicks for vulture
|
||||
n_clicks = n_clicks
|
||||
# Filter the data
|
||||
filtered_data = data.copy()
|
||||
# For all the data, we will add to the status column the value 'MUTED (FAIL)' and 'MUTED (PASS)' depending on the value of the column 'STATUS' and 'MUTED'
|
||||
if "MUTED" in filtered_data.columns:
|
||||
@@ -310,28 +325,21 @@ def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
for file in csv_files:
|
||||
df = pd.read_csv(file, sep=";", on_bad_lines="skip")
|
||||
if "CHECK_ID" in df.columns:
|
||||
# This handles the case where we are using v3 outputs
|
||||
if "TIMESTAMP" not in df.columns:
|
||||
# Rename the column 'ASSESSMENT_START_TIME' to 'TIMESTAMP'
|
||||
df.rename(columns={"ASSESSMENT_START_TIME": "TIMESTAMP"}, inplace=True)
|
||||
df["TIMESTAMP"] = df["TIMESTAMP"].str.replace("T", " ")
|
||||
elif "ASSESSMENT_START_TIME" in df.columns:
|
||||
df["ASSESSMENT_START_TIME"] = df["ASSESSMENT_START_TIME"].str.replace(
|
||||
"T", " "
|
||||
)
|
||||
# for each row, we are going to take the ASSESMENT_START_TIME if is not null and put it in the TIMESTAMP column
|
||||
df["TIMESTAMP"] = df.apply(
|
||||
lambda x: (
|
||||
x["ASSESSMENT_START_TIME"]
|
||||
if pd.isnull(x["TIMESTAMP"])
|
||||
else x["TIMESTAMP"]
|
||||
),
|
||||
axis=1,
|
||||
)
|
||||
df["TIMESTAMP"] = pd.to_datetime(df["TIMESTAMP"])
|
||||
df["TIMESTAMP"] = df["TIMESTAMP"].dt.strftime("%Y-%m-%d")
|
||||
if df["TIMESTAMP"][0] == updated_assessment_value:
|
||||
list_files.append(file)
|
||||
if "TIMESTAMP" in df.columns or df["PROVIDER"].unique() == "aws":
|
||||
# This handles the case where we are using v3 outputs
|
||||
if "TIMESTAMP" not in df.columns and df["PROVIDER"].unique() == "aws":
|
||||
# Rename the column 'ASSESSMENT_START_TIME' to 'TIMESTAMP'
|
||||
df["ASSESSMENT_START_TIME"] = df[
|
||||
"ASSESSMENT_START_TIME"
|
||||
].str.replace("T", " ")
|
||||
df.rename(
|
||||
columns={"ASSESSMENT_START_TIME": "TIMESTAMP"}, inplace=True
|
||||
)
|
||||
df["TIMESTAMP"] = df["TIMESTAMP"].str.replace("T", " ")
|
||||
df["TIMESTAMP"] = pd.to_datetime(df["TIMESTAMP"])
|
||||
df["TIMESTAMP"] = df["TIMESTAMP"].dt.strftime("%Y-%m-%d")
|
||||
if df["TIMESTAMP"][0] == updated_assessment_value:
|
||||
list_files.append(file)
|
||||
# append all the names of the files
|
||||
files_names = []
|
||||
for file in list_files:
|
||||
@@ -418,18 +426,16 @@ def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
# Filter REGION
|
||||
|
||||
# Check if filtered data contains an aws account
|
||||
# TODO - Handle azure locations
|
||||
if "LOCATION" in filtered_data.columns:
|
||||
filtered_data.rename(columns={"LOCATION": "REGION"}, inplace=True)
|
||||
if "REGION" not in filtered_data.columns:
|
||||
filtered_data["REGION"] = "-"
|
||||
if "LOCATION" in filtered_data.columns:
|
||||
filtered_data.rename(columns={"LOCATION": "REGION"}, inplace=True)
|
||||
if region_account_values == ["All"]:
|
||||
updated_region_account_values = filtered_data["REGION"].unique()
|
||||
elif "All" in region_account_values and len(region_account_values) > 1:
|
||||
# Remove 'All' from the list
|
||||
region_account_values.remove("All")
|
||||
updated_region_account_values = region_account_values
|
||||
|
||||
elif len(region_account_values) == 0:
|
||||
updated_region_account_values = filtered_data["REGION"].unique()
|
||||
region_account_values = ["All"]
|
||||
@@ -574,9 +580,11 @@ def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
"PASS": "#54d283",
|
||||
"INFO": "#2684FF",
|
||||
"MANUAL": "#636c78",
|
||||
"WARNING": "#fca903",
|
||||
"MUTED (FAIL)": "#fca903",
|
||||
"MUTED (PASS)": "#03fccf",
|
||||
"MUTED (MANUAL)": "#b33696",
|
||||
"MUTED (WARNING)": "#c7a45d",
|
||||
}
|
||||
# Define custom colors
|
||||
color_mapping = {
|
||||
@@ -617,13 +625,8 @@ def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
style={"height": "300px", "overflow-y": "auto"},
|
||||
)
|
||||
|
||||
# Figure for the bar chart
|
||||
|
||||
color_bars = [
|
||||
color_mapping["critical"],
|
||||
color_mapping["high"],
|
||||
color_mapping["medium"],
|
||||
color_mapping["low"],
|
||||
color_mapping[severity] for severity in df1["SEVERITY"].value_counts().index
|
||||
]
|
||||
|
||||
figure_bars = go.Figure(
|
||||
@@ -651,11 +654,17 @@ def filter_data(cloud_account_values, region_account_values, assessment_value):
|
||||
)
|
||||
|
||||
# TABLE
|
||||
severity_dict = {"critical": 3, "high": 2, "medium": 1, "low": 0}
|
||||
severity_dict = {
|
||||
"critical": 4,
|
||||
"high": 3,
|
||||
"medium": 2,
|
||||
"low": 1,
|
||||
"informational": 0,
|
||||
}
|
||||
fails_findings["SEVERITY"] = fails_findings["SEVERITY"].map(severity_dict)
|
||||
fails_findings = fails_findings.sort_values(by=["SEVERITY"], ascending=False)
|
||||
fails_findings["SEVERITY"] = fails_findings["SEVERITY"].replace(
|
||||
{3: "critical", 2: "high", 1: "medium", 0: "low"}
|
||||
{4: "critical", 3: "high", 2: "medium", 1: "low", 0: "informational"}
|
||||
)
|
||||
table_data = fails_findings.copy()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user