Skip to content

Commit

Permalink
new: [website] Experiment a bubble chart for the home page in order t…
Browse files Browse the repository at this point in the history
…o display the evolution of the last month.
  • Loading branch information
cedricbonhomme committed Dec 31, 2024
1 parent be7d6a9 commit 2574f33
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 8 deletions.
114 changes: 114 additions & 0 deletions website/web/static/js/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,117 @@ function drawBarChartHomePage(sightings, chartDivId, title, backgroundColor) {
},
});
}


function drawBubbleChartHomePage(sightings, chartDivId) {
// Process the data
const processedData = {};
const vulnerabilities = new Set(); // Collect all unique vulnerability IDs
sightings.forEach(entry => {
const date = new Date(entry.creation_timestamp);
const day = date.getDate(); // Extract day of the month
const vuln = entry.vulnerability;

vulnerabilities.add(vuln); // Track unique vulnerabilities

if (!processedData[vuln]) processedData[vuln] = {};
if (!processedData[vuln][day]) processedData[vuln][day] = 0;

processedData[vuln][day]++;
});

// Convert vulnerabilities to an array and assign each a y-axis index
const vulnerabilitiesArray = Array.from(vulnerabilities);

// Prepare datasets
const datasets = vulnerabilitiesArray.map((vuln, index) => ({
label: vuln, // Vulnerability ID
data: Object.entries(processedData[vuln] || {}).map(([day, count]) => ({
x: parseInt(day), // Day of the month
y: index, // Use the explicit index for this vulnerability
r: count * 5 // Bubble size based on count
})),
backgroundColor: `rgba(75, 192, 192, 0.6)` // Single color for all bubbles
}));

// SAdjust the chart's height dynamically
const chartCanvas = document.getElementById(chartDivId);
const dynamicHeight = Math.max(400, vulnerabilitiesArray.length * 25); // Base height + 20px per vulnerability
chartCanvas.style.height = `${dynamicHeight}px`;

// Render the bubble chart
const ctx = chartCanvas.getContext('2d');
new Chart(ctx, {
type: 'bubble',
data: { datasets },
options: {
plugins: {
legend: {
display: false // Hide legend
},
tooltip: {
callbacks: {
label: function (context) {
const vuln = datasets[context.datasetIndex].label;
const count = context.raw.r / 5; // Reverse the bubble size scaling
return `${vuln}: ${count} sightings`;
}
}
}
},
scales: {
x: {
title: {
display: true,
text: 'Days of the Month'
},
ticks: {
stepSize: 1
}
},
y: {
title: {
display: true,
text: 'Vulnerabilities'
},
ticks: {
stepSize: 1, // Ensures each tick corresponds to a vulnerability
callback: function (value) {
return vulnerabilitiesArray[value] || ''; // Map y-axis index to vulnerability ID
}
}
}
},
onClick: (event, elements) => {
if (elements.length > 0) {
const datasetIndex = elements[0].datasetIndex;
const vuln = datasets[datasetIndex].label;

// Redirect to the vulnerability details page
window.location.href = `/vuln/${vuln}#sightings`;
}
}
},
plugins: [{
id: 'bubbleLabels',
afterDraw(chart) {
const ctx = chart.ctx;
chart.data.datasets.forEach((dataset, datasetIndex) => {
dataset.data.forEach((dataPoint, index) => {
const meta = chart.getDatasetMeta(datasetIndex);
const position = meta.data[index].tooltipPosition();

// Draw the count inside the bubble
ctx.save();
ctx.fillStyle = 'black'; // Text color
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(Math.round(dataPoint.r / 5), position.x, position.y);
ctx.restore();
});
});
}
}]
});
}
38 changes: 30 additions & 8 deletions website/web/templates/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,25 @@
{% if not vulnerability_id and not vendor %}
<div class="container-fluid pt-5">
<div class="row justify-content-between">
<div class="col-md text-start">
<div id="sightingsChartContainerExploited" class="chart-container">
<canvas id="exploitedVulnsChartExploited" height="300"></canvas>
</div>
<div class="col-md text-start">
<div id="sightingsChartContainerExploited" class="chart-container">
<canvas id="exploitedVulnsChartExploited" height="300"></canvas>
</div>
<div class="col-md text-end">
<div id="sightingsChartContainerConfirmed" class="chart-container">
<canvas id="exploitedVulnsChartConfirmed" height="300"></canvas>
</div>
</div>
<div class="col-md text-end">
<div id="sightingsChartContainerConfirmed" class="chart-container">
<canvas id="exploitedVulnsChartConfirmed" height="300"></canvas>
</div>
</div>
</div>
<div class="row justify-content-between">
<h4>Evolution for the last month</h4>
<div class="col">
<canvas id="bubbleChart" width="800" height="400"></canvas>
</div>
</div>
</div>

{% endif %}

{% if source %}
Expand Down Expand Up @@ -393,6 +400,21 @@ <h5>All the vulnerabilites related to {{vendor}} - {{product}}</h5>
.catch((error) => {
console.error('Error:', error);
});


fetch("{{ url_for('apiv1.sighting_sightings_list') }}?date_from="+getDateSinceToday(31)+"&date_to="+getDateSinceToday(0))
.then(response => response.json())
.then(result => {
if (result.metadata.count == 0) {
document.getElementById("bubbleChart").style.display = 'none';
} else {
document.getElementById("bubbleChart").style.display = 'block';
drawBubbleChartHomePage(result.data, 'bubbleChart');
}
})
.catch((error) => {
console.error('Error:', error);
});
};

function loadComments(date_from, date_to) {
Expand Down

0 comments on commit 2574f33

Please sign in to comment.