mirror of
https://github.com/msoedov/agentic_security.git
synced 2026-09-30 11:51:46 +02:00
fix(report):
This commit is contained in:
1 parent
8d6618487f
commit
b3cccc75f5
2 files changed
+128
-45
No files matched your search
@@ -1,74 +1,157 @@
|
|||||||
from io import BytesIO
|
import io
|
||||||
from textwrap import wrap
|
import math
|
||||||
|
import string
|
||||||
|
|
||||||
import matplotlib as mpl
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from matplotlib.cm import ScalarMappable
|
from matplotlib.cm import ScalarMappable
|
||||||
|
from matplotlib.colors import LinearSegmentedColormap, Normalize
|
||||||
|
|
||||||
|
|
||||||
def plot_security_report(table):
|
def plot_security_report(table):
|
||||||
|
# Data preprocessing
|
||||||
data = pd.DataFrame(table)
|
data = pd.DataFrame(table)
|
||||||
|
|
||||||
# Sorting by failureRate for a meaningful arrangement
|
# Sort by failure rate and reset index
|
||||||
data_sorted = data.sort_values("failureRate", ascending=False)
|
data = data.sort_values("failureRate", ascending=False).reset_index(drop=True)
|
||||||
|
data["identifier"] = generate_identifiers(data)
|
||||||
|
|
||||||
# Values for the plot
|
# Plot setup
|
||||||
angles = np.linspace(0, 2 * np.pi, len(data_sorted), endpoint=False)
|
fig, ax = plt.subplots(figsize=(12, 10), subplot_kw={"projection": "polar"})
|
||||||
failure_rate = data_sorted["failureRate"]
|
fig.set_facecolor("#f0f0f0")
|
||||||
tokens = data_sorted["tokens"]
|
ax.set_facecolor("#f0f0f0")
|
||||||
|
|
||||||
# Styling parameters
|
# Styling parameters
|
||||||
COLORS = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"]
|
colors = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"][::-1] # Pastel palette
|
||||||
cmap = mpl.colors.LinearSegmentedColormap.from_list("custom", COLORS, N=256)
|
# colors = ["#440154", "#3b528b", "#21908c", "#5dc863"] # Viridis-inspired palette
|
||||||
norm = mpl.colors.Normalize(vmin=tokens.min(), vmax=tokens.max())
|
cmap = LinearSegmentedColormap.from_list("custom", colors, N=256)
|
||||||
|
norm = Normalize(vmin=data["tokens"].min(), vmax=data["tokens"].max())
|
||||||
|
|
||||||
# Polar plot setup
|
# Compute angles for the polar plot
|
||||||
fig, ax = plt.subplots(figsize=(10, 8), subplot_kw={"projection": "polar"})
|
angles = np.linspace(0, 2 * np.pi, len(data), endpoint=False)
|
||||||
ax.set_theta_offset(np.pi / 2)
|
|
||||||
ax.set_theta_direction(-1)
|
# Plot bars
|
||||||
ax.set_facecolor("white")
|
|
||||||
# Bars for failureRate with colors based on 'tokens'
|
|
||||||
bars = ax.bar(
|
bars = ax.bar(
|
||||||
angles,
|
angles,
|
||||||
failure_rate,
|
data["failureRate"],
|
||||||
width=0.3,
|
width=0.5,
|
||||||
color=[cmap(norm(t)) for t in tokens],
|
color=[cmap(norm(t)) for t in data["tokens"]],
|
||||||
alpha=0.75,
|
alpha=0.8,
|
||||||
label="Failure Rate %",
|
label="Failure Rate %",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add labels for the modules
|
# Customize polar plot
|
||||||
module_labels = ["\n".join(wrap(m, 10)) for m in data_sorted["module"]]
|
ax.set_theta_offset(np.pi / 2)
|
||||||
|
ax.set_theta_direction(-1)
|
||||||
|
ax.set_ylim(0, max(data["failureRate"]) * 1.1) # Add some headroom
|
||||||
|
|
||||||
|
# Add labels (now using identifiers)
|
||||||
ax.set_xticks(angles)
|
ax.set_xticks(angles)
|
||||||
|
ax.set_xticklabels(data["identifier"], fontsize=10, fontweight="bold")
|
||||||
|
|
||||||
# Add dashed vertical lines. These are just references
|
# Add circular grid lines
|
||||||
|
ax.yaxis.grid(True, color="gray", linestyle=":", alpha=0.5)
|
||||||
|
ax.set_yticks(np.arange(0, max(data["failureRate"]), 20))
|
||||||
|
ax.set_yticklabels(
|
||||||
|
[f"{x}%" for x in range(0, int(max(data["failureRate"])), 20)], fontsize=8
|
||||||
|
)
|
||||||
|
|
||||||
ax.set_xticklabels(module_labels, fontsize=7, color="#333")
|
# Add radial lines
|
||||||
|
ax.vlines(
|
||||||
|
angles,
|
||||||
|
0,
|
||||||
|
max(data["failureRate"]) * 1.1,
|
||||||
|
color="gray",
|
||||||
|
linestyle=":",
|
||||||
|
alpha=0.5,
|
||||||
|
)
|
||||||
|
|
||||||
# Color bar for the tokens
|
# Color bar for token count
|
||||||
sm = ScalarMappable(cmap=cmap, norm=norm)
|
sm = ScalarMappable(cmap=cmap, norm=norm)
|
||||||
sm.set_array([])
|
sm.set_array([])
|
||||||
cbar = plt.colorbar(sm, ax=ax, orientation="horizontal", pad=0.1)
|
cbar = fig.colorbar(sm, ax=ax, orientation="horizontal", pad=0.08, aspect=30)
|
||||||
cbar.set_label("Token Count (k)", fontsize=12, color="#444")
|
cbar.set_label("Token Count (k)", fontsize=10, fontweight="bold")
|
||||||
|
|
||||||
# Grid and legend
|
|
||||||
ax.grid(True, color="gray", linestyle=":", linewidth=0.5)
|
|
||||||
plt.legend(loc="upper right", bbox_to_anchor=(1.1, 1.1))
|
|
||||||
ax.vlines(angles, 0, 100, color="#444", ls=(0, (4, 4)), zorder=11)
|
|
||||||
|
|
||||||
# Title and subtitle
|
|
||||||
title = "Security Report for Different Modules"
|
|
||||||
# fig.suptitle(title, fontsize=18, weight="bold", ha="center", va="top")
|
|
||||||
|
|
||||||
|
# Title and caption
|
||||||
|
fig.suptitle(
|
||||||
|
"Security Report for Different Modules", fontsize=16, fontweight="bold", y=1.02
|
||||||
|
)
|
||||||
caption = "Report generated by https://github.com/msoedov/agentic_security"
|
caption = "Report generated by https://github.com/msoedov/agentic_security"
|
||||||
|
fig.text(
|
||||||
|
0.5,
|
||||||
|
0.02,
|
||||||
|
caption,
|
||||||
|
fontsize=8,
|
||||||
|
ha="center",
|
||||||
|
va="bottom",
|
||||||
|
alpha=0.7,
|
||||||
|
fontweight="bold",
|
||||||
|
)
|
||||||
|
|
||||||
fig.text(0.5, 0.025, caption, fontsize=10, ha="center", va="baseline")
|
# Add failure rate values on the bars
|
||||||
|
for angle, radius, bar, identifier in zip(
|
||||||
|
angles, data["failureRate"], bars, data["identifier"]
|
||||||
|
):
|
||||||
|
ax.text(
|
||||||
|
angle,
|
||||||
|
radius,
|
||||||
|
f"{identifier}: {radius:.1f}%",
|
||||||
|
ha="center",
|
||||||
|
va="bottom",
|
||||||
|
rotation=angle * 180 / np.pi - 90,
|
||||||
|
rotation_mode="anchor",
|
||||||
|
fontsize=7,
|
||||||
|
fontweight="bold",
|
||||||
|
color="black",
|
||||||
|
)
|
||||||
|
|
||||||
buf = BytesIO()
|
# Add a table with identifiers and dataset names
|
||||||
plt.savefig(buf, format="jpeg")
|
table_data = [["Threat"]] + [
|
||||||
|
[f"{identifier}: {module} ({fr:.1f}%)"]
|
||||||
|
for identifier, fr, module in zip(
|
||||||
|
data["identifier"], data["failureRate"], data["module"]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
table = ax.table(
|
||||||
|
cellText=table_data,
|
||||||
|
loc="right",
|
||||||
|
cellLoc="left",
|
||||||
|
)
|
||||||
|
table.auto_set_font_size(False)
|
||||||
|
table.set_fontsize(8)
|
||||||
|
|
||||||
|
# Adjust table style
|
||||||
|
table.scale(1, 0.7)
|
||||||
|
|
||||||
|
for (row, col), cell in table.get_celld().items():
|
||||||
|
cell.set_edgecolor("none")
|
||||||
|
cell.set_facecolor("#f0f0f0" if row % 2 == 0 else "#e0e0e0")
|
||||||
|
cell.set_alpha(0.8)
|
||||||
|
cell.set_text_props(wrap=True)
|
||||||
|
if row == 0:
|
||||||
|
cell.set_text_props(fontweight="bold")
|
||||||
|
|
||||||
|
# Adjust layout and save
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
buf = io.BytesIO()
|
||||||
|
plt.savefig(buf, format="png", dpi=300, bbox_inches="tight")
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
buf.seek(0)
|
buf.seek(0)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
||||||
|
def generate_identifiers(data):
|
||||||
|
data_length = len(data)
|
||||||
|
alphabet = string.ascii_uppercase
|
||||||
|
num_letters = len(alphabet)
|
||||||
|
|
||||||
|
identifiers = []
|
||||||
|
for i in range(data_length):
|
||||||
|
letter_index = i // num_letters
|
||||||
|
number = (i % num_letters) + 1
|
||||||
|
identifier = f"{alphabet[letter_index]}{number}"
|
||||||
|
identifiers.append(identifier)
|
||||||
|
|
||||||
|
return identifiers
|
||||||
@@ -294,9 +294,7 @@
|
|||||||
<div id="progress"
|
<div id="progress"
|
||||||
class="w-24 bg-earth-1 rounded-full h-2 overflow-hidden"
|
class="w-24 bg-earth-1 rounded-full h-2 overflow-hidden"
|
||||||
v-bind:style="{width: progressWidth}">
|
v-bind:style="{width: progressWidth}">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<img :src="imageUrl" alt="Generated Plot">
|
|
||||||
<div
|
<div
|
||||||
class="rounded-lg border bg-card text-card-foreground shadow-sm"
|
class="rounded-lg border bg-card text-card-foreground shadow-sm"
|
||||||
data-v0-t="card">
|
data-v0-t="card">
|
||||||
@@ -356,6 +354,7 @@
|
|||||||
class="inline-flex text-gray-100 items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-earth-1 text-earth-foreground hover:bg-earth-1/90 h-10 px-4 py-2">
|
class="inline-flex text-gray-100 items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-earth-1 text-earth-foreground hover:bg-earth-1/90 h-10 px-4 py-2">
|
||||||
Download failures
|
Download failures
|
||||||
</button>
|
</button>
|
||||||
|
<img :src="reportImageUrl" alt="Generated Plot" v-if="reportImageUrl">
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -556,6 +555,7 @@ Content-Type: application/json
|
|||||||
console.log('New event');
|
console.log('New event');
|
||||||
// { "module": "Module 49", "tokens": 480, "cost": 4.800000000000001, "progress": 9.8 }
|
// { "module": "Module 49", "tokens": 480, "cost": 4.800000000000001, "progress": 9.8 }
|
||||||
let progress = event.progress;
|
let progress = event.progress;
|
||||||
|
progress = progress % 100;
|
||||||
this.progressWidth = `${progress}%`;
|
this.progressWidth = `${progress}%`;
|
||||||
|
|
||||||
if (this.mainTable.length < 1) {
|
if (this.mainTable.length < 1) {
|
||||||
@@ -574,7 +574,7 @@ Content-Type: application/json
|
|||||||
last.last = false;
|
last.last = false;
|
||||||
this.mainTable.push(event);
|
this.mainTable.push(event);
|
||||||
event.last = true;
|
event.last = true;
|
||||||
// this.newRow()
|
this.newRow()
|
||||||
}
|
}
|
||||||
this.okMsg = `New event: ${event.module}: ${event.progress}%`;
|
this.okMsg = `New event: ${event.module}: ${event.progress}%`;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user