Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alert status trend #42

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vss-extension-dev.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifestVersion": 1,
"id": "GHAzDoWidget-DEV",
"version": "0.2.404",
"version": "0.2.415",
"public": false,
"name": "Advanced Security dashboard Widgets [DEV]",
"description": "[DEV] GitHub Advanced Security for Azure DevOps dashboard widgets",
Expand Down
2 changes: 1 addition & 1 deletion vss-extension.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifestVersion": 1,
"id": "GHAzDoWidget",
"version": "0.0.1.17",
"version": "0.0.1.19",
"public": true,
"name": "Advanced Security dashboard Widgets",
"description": "GitHub Advanced Security for Azure DevOps dashboard widgets",
Expand Down
93 changes: 73 additions & 20 deletions widgets/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async function storeAlerts(repoId, alertResult) {
}
}

async function getAlertsTrendLines(organization, projectName, repoId, daysToGoBack, summaryBucket) {
async function getAlertsTrendLines(organization, projectName, repoId, daysToGoBack, summaryBucket, alertType, overviewType = false, showClosed = false) {
consoleLog(`getAlertsTrend for organization [${organization}], project [${projectName}], repo [${repoId}]`)
try {
alertResult = null
Expand All @@ -214,23 +214,40 @@ async function getAlertsTrendLines(organization, projectName, repoId, daysToGoBa
codeAlertsTrend: []
}
}
if (overviewType === false) {
consoleLog('Loading all alerts trend lines')
// load the Secret alerts and create a trend line over the last 3 weeks
const secretAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.SECRET.name)
const secretAlertsTrend = getAlertsTrendLine(secretAlerts, AlertType.SECRET.name, daysToGoBack, summaryBucket)
consoleLog('')
// load the Dependency alerts and create a trend line over the last 3 weeks
const dependencyAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.DEPENDENCY.name)
const dependencyAlertsTrend = getAlertsTrendLine(dependencyAlerts, AlertType.DEPENDENCY.name, daysToGoBack, summaryBucket)
consoleLog('')
// load the Code alerts and create a trend line over the last 3 weeks
const codeAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.CODE.name)
const codeAlertsTrend = getAlertsTrendLine(codeAlerts, AlertType.CODE.name, daysToGoBack, summaryBucket)

// load the Secret alerts and create a trend line over the last 3 weeks
const secretAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.SECRET.name)
const secretAlertsTrend = getAlertsTrendLine(secretAlerts, AlertType.SECRET.name, daysToGoBack, summaryBucket)
consoleLog('')
// load the Dependency alerts and create a trend line over the last 3 weeks
const dependencyAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.DEPENDENCY.name)
const dependencyAlertsTrend = getAlertsTrendLine(dependencyAlerts, AlertType.DEPENDENCY.name, daysToGoBack, summaryBucket)
consoleLog('')
// load the Code alerts and create a trend line over the last 3 weeks
const codeAlerts = alertResult.value.filter(alert => alert.alertType === AlertType.CODE.name)
const codeAlertsTrend = getAlertsTrendLine(codeAlerts, AlertType.CODE.name, daysToGoBack, summaryBucket)
return {
secretAlertsTrend: secretAlertsTrend,
dependencyAlertsTrend: dependencyAlertsTrend,
codeAlertsTrend: codeAlertsTrend
}
}
else {
consoleLog(`Loading alerts trend lines for alert type: [$alertType.name}]`)
// filter the alerts based on the alertType
const alerts = alertResult.value.filter(alert => alert.alertType === alertType.name)
// create a trend line over the last 3 weeks for number of open alerts
const alertsOpenTrend = getAlertsTrendLine(alerts, alertType.name, daysToGoBack, summaryBucket, 'open')
const alertsDismissedTrend = getAlertsTrendLine(alerts, alertType.name, daysToGoBack, summaryBucket, 'dismissed')
const alertFixedTrend = getAlertsTrendLine(alerts, alertType.name, daysToGoBack, summaryBucket, 'fixed')

return {
secretAlertsTrend: secretAlertsTrend,
dependencyAlertsTrend: dependencyAlertsTrend,
codeAlertsTrend: codeAlertsTrend
return {
alertsOpenTrend: alertsOpenTrend,
alertsDismissedTrend: alertsDismissedTrend,
alertFixedTrend: alertFixedTrend
}
}
}
catch (err) {
Expand Down Expand Up @@ -258,7 +275,29 @@ function checkAlertActiveOnDate(alert, dateStr) {
return seenClosed;
}

function getAlertsTrendLine(alerts, type, daysToGoBack = 21, summaryBucket = 1) {
function checkAlertDismissedOnDate(alert, dateStr) {
// check if the alert.firstSeenDate is within the date range
// and if fixedDate is not set or is after the date range
const seenClosed = (alert.firstSeenDate.split('T')[0] <= dateStr && (!alert.fixedDate || alert.fixedDate.split('T')[0] > dateStr));
if (seenClosed) {
// check the dismissal.requestedOn date as well
if (alert.dismissal && alert.dismissal.requestedOn) {
const dismissed = (alert.dismissal.requestedOn.split('T')[0] <= dateStr);
return dismissed;
}
}

return false;
}

function checkAlertFixedOnDate(alert, dateStr) {
// check if the alert.firstSeenDate is within the date range
// and if fixedDate is not set or is after the date range
const seenClosed = (alert.firstSeenDate.split('T')[0] <= dateStr && (alert.fixedDate && alert.fixedDate.split('T')[0] < dateStr));
return seenClosed;
}

function getAlertsTrendLine(alerts, type, daysToGoBack = 21, summaryBucket = 1, filter = 'open') {
consoleLog(`getAlertsTrendLine for type ${type}`);

const trendLine = [];
Expand All @@ -270,9 +309,23 @@ function getAlertsTrendLine(alerts, type, daysToGoBack = 21, summaryBucket = 1)
for (let d = startDate; d <= today; d.setDate(d.getDate() + summaryBucket)) {
const date = new Date(d);
const dateStr = date.toISOString().split('T')[0];

const alertsOnDate = alerts.filter(alert => checkAlertActiveOnDate(alert, dateStr));
console.log(`On [${dateStr}] there were [${alertsOnDate.length}] active ${type} alerts`);
let alertsOnDate = []
switch (filter) {
case 'open':
alertsOnDate = alerts.filter(alert => checkAlertActiveOnDate(alert, dateStr));
break;
case 'dismissed':
alertsOnDate = alerts.filter(alert => checkAlertDismissedOnDate(alert, dateStr));
break;
case 'fixed':
alertsOnDate = alerts.filter(alert => checkAlertFixedOnDate(alert, dateStr));
break;
default:
// get all active alerts on this date
alertsOnDate = alerts.filter(alert => checkAlertActiveOnDate(alert, dateStr));
break;
}
console.log(`On [${dateStr}] there were [${alertsOnDate.length}] [${filter}] [${type}] alerts`);
trendLine.push({
date: dateStr,
count: alertsOnDate.length
Expand Down
82 changes: 46 additions & 36 deletions widgets/widgets/chart/chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
})

VSS.require([
"TFS/Dashboards/WidgetHelpers",
Expand All @@ -26,85 +26,95 @@
return {
load: async function(widgetSettings) {
return Services.ChartsService.getService().then(async function(chartService){
consoleLog("Starting to create chart");
var $container = $('#Chart-Container');
var $title = $('h2.ghazdo-title');
consoleLog("Starting to create chart")
var $container = $('#Chart-Container')
var $title = $('h2.ghazdo-title')

const webContext = VSS.getWebContext();
const project = webContext.project;
const organization = webContext.account.name;
const projectId = project.id;
const webContext = VSS.getWebContext()
const project = webContext.project
const organization = webContext.account.name
const projectId = project.id
// convert project.name to url encoding
const projectName = project.name.replace(/ /g, "%20").replace(/&/g, "%26");
const projectName = project.name.replace(/ /g, "%20").replace(/&/g, "%26")

consoleLog('project id: ' + projectId);
consoleLog('project name: ' + projectName);
consoleLog('organization name: ' + organization);
consoleLog('project id: ' + projectId)
consoleLog('project name: ' + projectName)
consoleLog('organization name: ' + organization)

consoleLog(`WidgetSettings inside loadChartWidget_2x2: ${JSON.stringify(widgetSettings)}`);
consoleLog(`WidgetSettings inside loadChartWidget_2x2: ${JSON.stringify(widgetSettings)}`)

// data contains a stringified json object, so we need to make a json object from it
const data = JSON.parse(widgetSettings.customSettings.data);
const data = JSON.parse(widgetSettings.customSettings.data)

let repoName
let repoId
// init empty object first
let alertTrendLines = {secretAlertTrend: [], dependencyAlertTrend: [], codeAlertsTrend: []};
let chartType = 1;
let alertTypeConfig = 1;
let alertTrendLines = {secretAlertTrend: [], dependencyAlertTrend: [], codeAlertsTrend: []}
let chartType = 1
let alertTypeConfig = 1
if (data && data.chartType && data.chartType !== "") {
chartType = data.chartType;
consoleLog('loaded chartType from widgetSettings: ' + chartType);
chartType = data.chartType
consoleLog('loaded chartType from widgetSettings: ' + chartType)
}
else {
consoleLog('chartType is not set, using default value: ' + chartType);
consoleLog('chartType is not set, using default value: ' + chartType)
}

if (data && data.alertType) {
alertTypeConfig = data.alertType;
consoleLog('loaded alertType from widgetSettings: ' + alertTypeConfig);
alertTypeConfig = data.alertType
consoleLog('loaded alertType from widgetSettings: ' + alertTypeConfig)
}

if (data && data.repo && data.repo !== "") {
repoName = data.repo;
repoId = data.repoId;
repoName = data.repo
repoId = data.repoId

$container.text(`${data.repo}`)

switch (chartType) {
case "3":
try {
const alertType = GetAlertTypeFromValue(alertTypeConfig)
$title.text(`${alertType.display} Alerts status trend`)
renderDurationChart({organization, projectName, repoId, $container, chartService, alertType, widgetSize: widgetSettings.size})
}
catch (err) {
consoleLog(`Error loading the alerts pie: ${err}`)
}
break
case "2":
try {
const alertType = GetAlertTypeFromValue(alertTypeConfig);
$title.text(`${alertType.display} Alerts by Severity`)
renderPieChart(organization, projectName, repoId, $container, chartService, alertType, widgetSettings.size);
}
catch (err) {
consoleLog(`Error loading the alerts pie: ${err}`);
consoleLog(`Error loading the alerts pie: ${err}`)
}
break;
break
default:
try {
$title.text(`Advanced Security Alerts Trend`)
renderTrendLine(organization, projectName, repoId, $container, chartService, widgetSettings.size);
$title.text(`Advanced Security alerts trend`)
renderTrendLine(organization, projectName, repoId, $container, chartService, widgetSettings.size)
}
catch (err) {
consoleLog(`Error loading the alerts trend: ${err}`);
consoleLog(`Error loading the alerts trend: ${err}`)
}
break;
break
}
}
else {
consoleLog('configuration is needed first, opening with empty values');
consoleLog('configuration is needed first, opening with empty values')
// set the tile to indicate config is needed
$title.text(`Configure the widget to get Advanced Security alerts trend information`);
$title.text(`Configure the widget to get Advanced Security alerts trend information`)
}

return WidgetHelpers.WidgetStatusHelper.Success();
});
return WidgetHelpers.WidgetStatusHelper.Success()
})
}
}
});
VSS.notifyLoadSucceeded();
})
VSS.notifyLoadSucceeded()
});
</script>

Expand Down
Loading