diff --git a/web/reNgine/charts.py b/web/reNgine/charts.py
new file mode 100644
index 000000000..546f09a62
--- /dev/null
+++ b/web/reNgine/charts.py
@@ -0,0 +1,194 @@
+import base64
+import colorsys
+
+import plotly.graph_objs as go
+from plotly.io import to_image
+from django.db.models import Count
+from reNgine.definitions import NUCLEI_SEVERITY_MAP
+
+from startScan.models import *
+
+
+
+"""
+ This file is used to generate the charts for the pdf report.
+"""
+
+def generate_subdomain_chart_by_http_status(subdomains):
+ """
+ Generates a donut chart using plotly for the subdomains based on the http status.
+ Includes label, count, and percentage inside the chart segments and in the legend.
+ Args:
+ subdomains: QuerySet of subdomains.
+ Returns:
+ Image as base64 encoded string.
+ """
+ http_statuses = (
+ subdomains
+ .exclude(http_status=0)
+ .values('http_status')
+ .annotate(count=Count('http_status'))
+ .order_by('-count')
+ )
+ http_status_count = [{'http_status': entry['http_status'], 'count': entry['count']} for entry in http_statuses]
+
+ total = sum(entry['count'] for entry in http_status_count)
+
+ labels = [str(entry['http_status']) for entry in http_status_count]
+ sizes = [entry['count'] for entry in http_status_count]
+ colors = [get_color_by_http_status(entry['http_status']) for entry in http_status_count]
+
+ text = [f"{label}
{size}
({size/total:.1%})" for label, size in zip(labels, sizes)]
+
+ fig = go.Figure(data=[go.Pie(
+ labels=labels,
+ values=sizes,
+ marker=dict(colors=colors),
+ hole=0.4,
+ textinfo="text",
+ text=text,
+ textposition="inside",
+ textfont=dict(size=10),
+ hoverinfo="label+percent+value"
+ )])
+
+ fig.update_layout(
+ title_text="",
+ annotations=[dict(text='HTTP Status', x=0.5, y=0.5, font_size=14, showarrow=False)],
+ showlegend=True,
+ margin=dict(t=60, b=60, l=60, r=60),
+ width=700,
+ height=700,
+ legend=dict(
+ font=dict(size=18),
+ orientation="v",
+ yanchor="middle",
+ y=0.5,
+ xanchor="left",
+ x=1.05
+ ),
+ )
+
+ img_bytes = to_image(fig, format="png")
+ img_base64 = base64.b64encode(img_bytes).decode('utf-8')
+ return img_base64
+
+
+
+def get_color_by_severity(severity_int):
+ """
+ Returns a color based on the severity level using a modern color scheme.
+ """
+ color_map = {
+ 4: '#FF4D6A',
+ 3: '#FF9F43',
+ 2: '#FFCA3A',
+ 1: '#4ADE80',
+ 0: '#4ECDC4',
+ -1: '#A8A9AD',
+ }
+ return color_map.get(severity_int, '#A8A9AD') # Default to gray if severity is unknown
+
+def generate_vulnerability_chart_by_severity(vulnerabilities):
+ """
+ Generates a donut chart using plotly for the vulnerabilities based on the severity.
+ Args:
+ vulnerabilities: QuerySet of Vulnerability objects.
+ Returns:
+ Image as base64 encoded string.
+ """
+ severity_counts = (
+ vulnerabilities
+ .values('severity')
+ .annotate(count=Count('severity'))
+ .order_by('-severity')
+ )
+
+ total = sum(entry['count'] for entry in severity_counts)
+
+ labels = [NUCLEI_REVERSE_SEVERITY_MAP[entry['severity']].capitalize() for entry in severity_counts]
+ values = [entry['count'] for entry in severity_counts]
+ colors = [get_color_by_severity(entry['severity']) for entry in severity_counts]
+
+ text = [f"{label}
{value}
({value/total:.1%})" for label, value in zip(labels, values)]
+
+ fig = go.Figure(data=[go.Pie(
+ labels=labels,
+ values=values,
+ marker=dict(colors=colors),
+ hole=0.4,
+ textinfo="text",
+ text=text,
+ textposition="inside",
+ textfont=dict(size=12),
+ hoverinfo="label+percent+value",
+ )])
+
+ fig.update_layout(
+ title_text="",
+ annotations=[dict(text='Severity', x=0.5, y=0.5, font_size=14, showarrow=False)],
+ showlegend=True,
+ margin=dict(t=60, b=60, l=60, r=60),
+ width=700,
+ height=700,
+ legend=dict(
+ font=dict(size=18),
+ orientation="v",
+ yanchor="middle",
+ y=0.5,
+ xanchor="left",
+ x=1.05
+ ),
+ )
+
+
+ img_bytes = to_image(fig, format="png")
+ img_base64 = base64.b64encode(img_bytes).decode('utf-8')
+ return img_base64
+
+
+
+def generate_color(base_color, offset):
+ r, g, b = int(base_color[1:3], 16), int(base_color[3:5], 16), int(base_color[5:7], 16)
+ factor = 1 + (offset * 0.03)
+ r, g, b = [min(255, int(c * factor)) for c in (r, g, b)]
+ return f"#{r:02x}{g:02x}{b:02x}"
+
+
+def get_color_by_http_status(http_status):
+ """
+ Returns the color based on the http status.
+ Args:
+ http_status: HTTP status code.
+ Returns:
+ Color code.
+ """
+
+ status = int(http_status)
+
+ colors = {
+ 200: "#36a2eb",
+ 300: "#4bc0c0",
+ 400: "#ff6384",
+ 401: "#ff9f40",
+ 403: "#f27474",
+ 404: "#ffa1b5",
+ 429: "#bf7bff",
+ 500: "#9966ff",
+ 502: "#8a4fff",
+ 503: "#c39bd3",
+ }
+
+
+ if status in colors:
+ return colors[status]
+ elif 200 <= status < 300:
+ return generate_color(colors[200], status - 200)
+ elif 300 <= status < 400:
+ return generate_color(colors[300], status - 300)
+ elif 400 <= status < 500:
+ return generate_color(colors[400], status - 400)
+ elif 500 <= status < 600:
+ return generate_color(colors[500], status - 500)
+ else:
+ return "#c9cbcf"
\ No newline at end of file
diff --git a/web/reNgine/utilities.py b/web/reNgine/utilities.py
index a3101625f..58a4503b0 100644
--- a/web/reNgine/utilities.py
+++ b/web/reNgine/utilities.py
@@ -159,3 +159,16 @@ def is_out_of_scope(self, subdomain):
if subdomain in self.plain_patterns:
return True
return any(pattern.search(subdomain) for pattern in self.regex_patterns)
+
+
+def sorting_key(subdomain):
+ # sort subdomains based on their http status code with priority 200 < 300 < 400 < rest
+ status = subdomain['http_status']
+ if 200 <= status <= 299:
+ return 1
+ elif 300 <= status <= 399:
+ return 2
+ elif 400 <= status <= 499:
+ return 3
+ else:
+ return 4
\ No newline at end of file
diff --git a/web/requirements.txt b/web/requirements.txt
index 9b53e163f..6fff50162 100644
--- a/web/requirements.txt
+++ b/web/requirements.txt
@@ -40,3 +40,5 @@ weasyprint==53.3
wafw00f==2.2.0
xmltodict==0.13.0
django-environ==0.11.2
+plotly==5.23.0
+kaleido
\ No newline at end of file
diff --git a/web/startScan/templates/startScan/history.html b/web/startScan/templates/startScan/history.html
index bb78cf07c..d15ffd022 100644
--- a/web/startScan/templates/startScan/history.html
+++ b/web/startScan/templates/startScan/history.html
@@ -198,7 +198,7 @@