Skip to content

Commit

Permalink
Refactor XLS export feature
Browse files Browse the repository at this point in the history
  • Loading branch information
fxbru committed Apr 5, 2024
1 parent f1a2de0 commit 832f748
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 164 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,3 @@ package-lock.json

dump.rdb

# excel generated
excel_save/*
1 change: 1 addition & 0 deletions app/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
STATUS_ABORTED = 5
PROJECTS_SRC_PATH = "data/projects/"
EXTRACT_FOLDER_NAME = "extract"
EXPORT_FOLDER_NAME = "exports"
SCC = "app/third-party/scc/scc"

# Vulnerability occurence Status
Expand Down
20 changes: 15 additions & 5 deletions app/projects/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,18 @@ class ProjectForm(FlaskForm):
validators=[FileRequired(), FileAllowed(["zip"], "Zip archives only")],
)

class ExcelForm(FlaskForm):
choice = RadioField('Select Option', choices=[('only_confirmed', 'Only Confirmed'),
('confirmed_and_undefined', 'Confirmed and To review'),
('all', 'All')])
submit = SubmitField('Submit')

class XLSExportForm(FlaskForm):
choice = RadioField(
"Filter findings to export",
choices=[
("only_confirmed", "Export only Confirmed vulnerabilities"),
(
"confirmed_and_undefined",
"Export Confirmed and To review vulnerabilities",
),
("all", "Export all vulnerabilities"),
],
default="all",
)
submit = SubmitField("Submit")
154 changes: 15 additions & 139 deletions app/projects/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import os
import pathlib

from openpyxl import Workbook
from openpyxl.styles import Alignment, PatternFill
from zipfile import BadZipFile, ZipFile

from flask import (
Expand All @@ -24,14 +22,19 @@
from werkzeug.utils import secure_filename

from app import db
from app.constants import EXTRACT_FOLDER_NAME, LANGUAGES_DEVICONS, PROJECTS_SRC_PATH
from app.constants import (
EXTRACT_FOLDER_NAME,
LANGUAGES_DEVICONS,
PROJECTS_SRC_PATH,
)
from app.base import util
from app.projects import blueprint
from app.projects.forms import ProjectForm, ExcelForm
from app.projects.forms import ProjectForm, XLSExportForm
from app.projects.models import Project
from app.projects.util import (
check_zipfile,
count_lines,
generate_xls,
has_access,
remove_project,
sha256sum,
Expand Down Expand Up @@ -69,13 +72,13 @@ def projects_dashboard(project_id):
if not has_access(current_user, project):
return render_template("403.html"), 403
# XLS export button
form = ExcelForm()
form = XLSExportForm()
if form.validate_on_submit():
selected_option = form.choice.data
# Faire quelque chose avec l'option sélectionnée, comme rediriger vers une autre page
return redirect(
url_for(
"projects_blueprint.scan_to_excel",
"projects_blueprint.xls_export",
project_id=project_id,
selected_option=selected_option,
)
Expand Down Expand Up @@ -112,11 +115,6 @@ def projects_remove(project_id):
if not has_access(current_user, project):
return render_template("403.html"), 403
remove_project(project)
# If there is project excel generated, delet it
path = os.getcwd()
print(path)
if os.path.exists(f"excel_save/{project.name}.xlsx"):
os.remove(f"excel_save/{project.name}.xlsx")
current_app.logger.info("Project deleted (project.id=%i)", project.id)
flash("Project successfully deleted", "success")
return redirect(url_for("projects_blueprint.projects_list"))
Expand Down Expand Up @@ -186,137 +184,15 @@ def projects_create():
return json.dumps(project_form.errors), 403


@blueprint.route("/projects/to_excel/<project_id>")
@blueprint.route("/projects/xls_export/<project_id>")
@login_required
def scan_to_excel(project_id):
def xls_export(project_id):
project = Project.query.filter_by(id=project_id).first_or_404()
# Check if the user has access to the project
if not has_access(current_user, project):
return render_template("403.html"), 403
wb = Workbook()
wb.is_spellcheck_enabled = False
vulnerabilities = project.analysis.vulnerabilities
# Generate XLS file
selected_option = request.args.get("selected_option")

# Create the sheets table
sheets = {}
# Set the first sheet
sheets["MainSheet"] = wb.active
sheets["MainSheet"].title = "MainSheet"
# Set all vulnerabilitie sheets
for index, vulnerabilitie in enumerate(vulnerabilities):
sheet_name = f"vulnerabilitie {vulnerabilitie.id}"
sheets[sheet_name] = wb.create_sheet(title=sheet_name)

# Set the MainSheet data
sheets["MainSheet"].merge_cells("A1:B1")
sheets["MainSheet"].merge_cells("A2:B2")
sheets["MainSheet"]["A1"] = "Project name"
sheets["MainSheet"]["A2"] = project.name
sheets["MainSheet"]["C1"] = "Project id"
sheets["MainSheet"]["C2"] = project.id
sheets["MainSheet"]["D1"] = "Risk level"
sheets["MainSheet"]["D2"] = project.risk_level
sheets["MainSheet"]["E1"] = "Lines"
sheets["MainSheet"]["E2"] = project.project_lines_count.total_line_count

# Set the vulnerabilites header
sheets["MainSheet"].row_dimensions[4].height = 40
sheets["MainSheet"].merge_cells("A4:C4")
sheets["MainSheet"].merge_cells("G4:J4")
sheets["MainSheet"].merge_cells("M4:V4")
for row in sheets["MainSheet"]["A4":"X4"]:
for cell in row:
# Center text
cell.alignment = Alignment(horizontal="center", vertical="center")
# Set cells background color
cell.fill = PatternFill(
start_color="DEE6BD", end_color="DEE6BD", fill_type="solid"
)
sheets["MainSheet"]["A4"] = "Title"
sheets["MainSheet"]["D4"] = "Id"
sheets["MainSheet"]["E4"] = "Confidence"
sheets["MainSheet"].column_dimensions["E"].width = 15
sheets["MainSheet"]["F4"] = "Severity"
sheets["MainSheet"]["G4"] = "Owasp"
sheets["MainSheet"]["K4"] = "Impact"
sheets["MainSheet"]["L4"] = "Likelihood"
sheets["MainSheet"].column_dimensions["L"].width = 15
sheets["MainSheet"]["M4"] = "Description"
sheets["MainSheet"]["W4"] = "Occurences"
sheets["MainSheet"].column_dimensions["W"].width = 15
sheets["MainSheet"]["X4"] = "Ctrl Click"
# Take all vulnerabilities
for index, vulnerabilitie in enumerate(vulnerabilities):
# Upgrade de rosheets["MainSheet"] size
sheets["MainSheet"].row_dimensions[5 + index].height = 40
# Set auto ligne return for description
sheets["MainSheet"][f"M{5 + index}"].alignment = Alignment(wrap_text=True)
# Set vulnerabilitie data
sheets["MainSheet"].merge_cells(f"A{5 + index}:C{5 + index}")
sheets["MainSheet"].merge_cells(f"G{5 + index}:J{5 + index}")
sheets["MainSheet"].merge_cells(f"M{5 + index}:V{5 + index}")
sheets["MainSheet"][f"A{5 + index}"] = vulnerabilitie.title
sheets["MainSheet"][f"D{5 + index}"] = vulnerabilitie.id
sheets["MainSheet"][f"E{5 + index}"] = vulnerabilitie.confidence
sheets["MainSheet"][f"F{5 + index}"] = vulnerabilitie.severity
sheets["MainSheet"][f"G{5 + index}"] = vulnerabilitie.owasp
sheets["MainSheet"][f"K{5 + index}"] = vulnerabilitie.impact
sheets["MainSheet"][f"L{5 + index}"] = vulnerabilitie.likelihood
sheets["MainSheet"][f"M{5 + index}"] = vulnerabilitie.description
sheets["MainSheet"][f"W{5 + index}"] = len(vulnerabilitie.occurences)
# Ceate the vulnerabilitie occurences sheets button
sheets["MainSheet"][f"X{5 + index}"] = f"{vulnerabilitie.id} details"
sheets["MainSheet"][
f"X{5 + index}"
].hyperlink = f"#'vulnerabilitie {vulnerabilitie.id}'!A1"

# Set the vulnerabilitie occurences sheets
for index, vulnerabilitie in enumerate(vulnerabilities):
# Set the mainSheet button
sheet_name = f"vulnerabilitie {vulnerabilitie.id}"
sheets[sheet_name].column_dimensions["A"].width = 15
sheets[sheet_name]["A1"] = "MainSheet"
# Set sheet data
sheets[sheet_name]["A1"].hyperlink = f"#'MainSheet'!A1"
sheets[sheet_name].column_dimensions["B"].width = 15
sheets[sheet_name]["B1"] = f"Vulnerabilitie {vulnerabilitie.id}"
sheets[sheet_name].merge_cells("C1:D1")
sheets[sheet_name].column_dimensions["C"].width = 15
sheets[sheet_name]["C1"] = vulnerabilitie.title

# Set occurences header
sheets[sheet_name].merge_cells("A3:D3")
sheets[sheet_name]["A3"] = "File path"
sheets[sheet_name]["E3"] = "Id"
sheets[sheet_name].merge_cells("F3:Z3")
sheets[sheet_name]["F3"] = "Match_string"

for row in sheets[sheet_name]["A3":"Z3"]:
for cell in row:
# Center header text
cell.alignment = Alignment(horizontal="center", vertical="center")
# Set cells background color
cell.fill = PatternFill(
start_color="DEE6BD", end_color="DEE6BD", fill_type="solid"
)

# Set occurences data
for index, occurence in enumerate(vulnerabilitie.occurences):
if (
(selected_option == "only_confirmed" and occurence.status == 1)
or (
selected_option == "confirmed_and_undefined"
and occurence.status in [1, 0]
)
or (selected_option == "all")
):
sheets[sheet_name].merge_cells(f"A{4 + index}:D{4 + index}")
sheets[sheet_name][f"A{4 + index}"] = occurence.file_path
sheets[sheet_name][f"E{4 + index}"] = occurence.id
sheets[sheet_name].merge_cells(f"F{4 + index}:Z{4 + index}")
sheets[sheet_name][f"F{4 + index}"] = occurence.match_string

wb.save(f"excel_save/{project.name}.xlsx")
# The excel file is deleted along with the proejct in /project/remove/<project_id>
return send_file(f"../excel_save/{project.name}.xlsx", as_attachment=True)
xls_path = generate_xls(project, selected_option)
# Return generated file to the browser
return send_file(xls_path, as_attachment=True)
29 changes: 14 additions & 15 deletions app/projects/templates/project_dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,12 @@ <h3 class="card-title">Risk level</h3>
<span title="Inspector" class="fas fa-list"></span>
Inspector
</a>
<a class="btn btn-default"
data-toggle="modal"
data-target="#confirm-generate-excel">
<span title="Download" class="fas fa-download"></span>
Excel
</a>
</div>
<a class="btn btn-default" data-toggle="modal"
data-target="#confirm-xls-export">
<span title="Download" class="fas fa-download"></span>
XLS Export
</a>
</div>
</div>
</div>
Expand All @@ -199,12 +198,12 @@ <h3 class="card-title">Project information</h3>
<div class="row">
<div class="col-md-12">
<span class="fas fa-user"></span>
Created by:
Created by:
{{ project.creator.username }}
</div>
<div class="col-md-12">
<span class="fas fa-clock"></span>
Analysis duration:
Analysis duration:
{% set duration = (project.analysis.finished_on - project.analysis.started_on) | string
%}
{{ duration.split('.', 2)[0] }}
Expand Down Expand Up @@ -239,7 +238,7 @@ <h3 class="card-title">Vulnerabilities summary</h3>
<i class="fas fa-minus"></i>
</button>
</div>
</div>
</div>
<div class="card-body" style="height: 21em;">
{% if project.analysis.vulnerabilities | length > 0 %}
<div style="height: 17em;">
Expand Down Expand Up @@ -358,12 +357,12 @@ <h3 class="card-title">Detected features</h3>
</section>
</div>

<!-- Excel Modal -->
<div class="modal fade" id="confirm-generate-excel" tabindex="-1" role="dialog" aria-hidden="true">
<!-- XLS Export Modal -->
<div class="modal fade" id="confirm-xls-export" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Generate excel scan</h5>
<h5 class="modal-title">Export analysis to XLS</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
Expand All @@ -378,13 +377,13 @@ <h5 class="modal-title">Generate excel scan</h5>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-outline btn-default" data-dismiss="modal">Cancel</button>
<button type="button submit" value="Submit" class="btn btn-default" >Generate</button>
<button type="button submit" value="Submit" class="btn btn-default">Export</button>
</div>
</form>
</div>
</div>
</div>
<!-- End excel modal -->
<!-- End XLS export modal -->
{% endblock content %}

<!-- Specific Page JS goes HERE -->
Expand All @@ -396,7 +395,7 @@ <h5 class="modal-title">Generate excel scan</h5>

const risk_level = {{ project.risk_level }};
const risk_color_hex = "{{ risk_color_hex }}";

const critical_count = {{ project.analysis.vulnerabilities | selectattr("severity", "equalto", "critical") | list | length }};
const high_count = {{ project.analysis.vulnerabilities | selectattr("severity", "equalto", "high") | list | length }};
const medium_count = {{ project.analysis.vulnerabilities | selectattr("severity", "equalto", "medium") | list | length }};
Expand Down
Loading

0 comments on commit 832f748

Please sign in to comment.