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

fix hub #41

Merged
merged 4 commits into from
Dec 16, 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
Binary file added img/hub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Install from the marketplace: https://marketplace.visualstudio.com/items?itemNam

> Note: only project level dashboards are supported at the moment.

## Dashboard
This extension also adds a dashboard to the menu that shows the overall status of the entire project. All alerts from all repositories are loaded and shown in a single dashboard.
![Screenshot of the hub in action](/img/hub.png)

## Pipeline tasks
* Advanced-Security-Review: lets you check the pull request for newly introduced alerts from Dependency Scanning or Code Scanning (configurable). If new alerts are introduced, the task will fail.
> Note:
Expand Down
5 changes: 2 additions & 3 deletions 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.397",
"version": "0.2.404",
"public": false,
"name": "Advanced Security dashboard Widgets [DEV]",
"description": "[DEV] GitHub Advanced Security for Azure DevOps dashboard widgets",
Expand Down Expand Up @@ -221,11 +221,10 @@
"type": "ms.vss-web.hub",
"description": "[DEV] GHAzDO Hub",
"targets": [
"ms.vss-work-web.work-hub-group",
"ms.vss-code-web.code-hub-group"
],
"properties": {
"name": "Advanced Security Dashboard",
"name": "Advanced Security [DEV]",
"uri": "widgets/widgets/hub/hub.html",
"iconName": "Shield",
"order": 99
Expand Down
16 changes: 15 additions & 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.14",
"version": "0.0.1.17",
"public": true,
"name": "Advanced Security dashboard Widgets",
"description": "GitHub Advanced Security for Azure DevOps dashboard widgets",
Expand Down Expand Up @@ -162,6 +162,20 @@
"uri": "widgets/widgets/chart/configuration_2x2.html"
}
},
{
"id": "GHAzDoWidget.Hub",
"type": "ms.vss-web.hub",
"description": "GHAzDO Hub",
"targets": [
"ms.vss-code-web.code-hub-group"
],
"properties": {
"name": "Advanced Security Dashboard",
"uri": "widgets/widgets/hub/hub.html",
"iconName": "Shield",
"order": 99
}
},
{
"id": "GHAzDoDependencyReviewTask",
"type": "ms.vss-distributed-task.task",
Expand Down
36 changes: 23 additions & 13 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) {
async function getAlertsTrendLines(organization, projectName, repoId, daysToGoBack, summaryBucket) {
consoleLog(`getAlertsTrend for organization [${organization}], project [${projectName}], repo [${repoId}]`)
try {
alertResult = null
Expand All @@ -203,19 +203,29 @@ async function getAlertsTrendLines(organization, projectName, repoId) {
alertResult = await authenticatedGet(url)
//consoleLog('alertResult: ' + JSON.stringify(alertResult))
}
consoleLog('alertResult count: ' + alertResult.count)
if (alertResult) {
consoleLog('alertResult count: ' + alertResult.count)
}
else {
consoleLog('alertResult is null')
return {
secretAlertsTrend: [],
dependencyAlertsTrend: [],
codeAlertsTrend: []
}
}

// 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, 'secret')
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, 'dependency')
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, 'code')
const codeAlertsTrend = getAlertsTrendLine(codeAlerts, AlertType.CODE.name, daysToGoBack, summaryBucket)

return {
secretAlertsTrend: secretAlertsTrend,
Expand Down Expand Up @@ -248,16 +258,16 @@ function checkAlertActiveOnDate(alert, dateStr) {
return seenClosed;
}

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

const trendLine = [];
const trendLineSimple = [];
const today = new Date();
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(today.getDate() - 21);
const startDate = new Date();
startDate.setDate(today.getDate() - daysToGoBack);

for (let d = threeWeeksAgo; d <= today; d.setDate(d.getDate() + 1)) {
for (let d = startDate; d <= today; d.setDate(d.getDate() + summaryBucket)) {
const date = new Date(d);
const dateStr = date.toISOString().split('T')[0];

Expand All @@ -275,13 +285,13 @@ function getAlertsTrendLine(alerts, type) {
return trendLineSimple;
}

function getDatePoints() {
function getDatePoints(daysToGoBack = 21, summaryBucket = 1) {
const trendDates = [];
const today = new Date();
const threeWeeksAgo = new Date();
threeWeeksAgo.setDate(today.getDate() - 21);
const startDate = new Date();
startDate.setDate(today.getDate() - daysToGoBack);

for (let d = threeWeeksAgo; d <= today; d.setDate(d.getDate() + 1)) {
for (let d = startDate; d <= today; d.setDate(d.getDate() + summaryBucket)) {
const date = new Date(d);
const dateStr = date.toISOString().split('T')[0];
trendDates.push(dateStr);
Expand Down
10 changes: 5 additions & 5 deletions widgets/widgets/chart/chart.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
async function createChart($container, chartService, alertTrendLines, widgetSize) {
async function createChart($container, chartService, alertTrendLines, widgetSize, daysToGoBack = 21, summaryBucket = 1) {

const datePoints = getDatePoints();
const datePoints = getDatePoints(daysToGoBack, summaryBucket);
var chartOptions = {
"hostOptions": {
"height": "290",
Expand Down Expand Up @@ -94,16 +94,16 @@ async function renderPieChart(organization, projectName, repoId, $container, cha
}
}

async function renderTrendLine(organization, projectName, repoId, $container, chartService, widgetSize) {
async function renderTrendLine(organization, projectName, repoId, $container, chartService, widgetSize, daysToGoBack = 21, summaryBucket = 1) {
consoleLog('renderTrendLine');
try {
// get the trend data for alerts first
const alertTrendLines = await getAlertsTrendLines(organization, projectName, repoId)
const alertTrendLines = await getAlertsTrendLines(organization, projectName, repoId, daysToGoBack, summaryBucket)
consoleLog('Dependencies AlertTrend: ' + JSON.stringify(alertTrendLines.dependencyAlertsTrend));
consoleLog('Code scanning AlertTrend: ' + JSON.stringify(alertTrendLines.codeAlertsTrend));
consoleLog('Secrets AlertTrend: ' + JSON.stringify(alertTrendLines.secretAlertsTrend));

createChart($container, chartService, alertTrendLines, widgetSize);
createChart($container, chartService, alertTrendLines, widgetSize, daysToGoBack, summaryBucket);
}
catch (err) {
consoleLog(`Error loading the alerts trend: ${err}`);
Expand Down
37 changes: 27 additions & 10 deletions widgets/widgets/hub/hub.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,18 @@
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 chartService = await Services.ChartsService.getService()

await createCharts({chartService, projectName, organization})
await createCharts({chartService, projectName: project.name, organization})

const debugging = document.getElementById('debugInfoCheckbox')
// wait at least 5 seconds before hiding the progress div
setTimeout(() => {
if (!debugging.checked) {
const progressDivContainer = document.getElementById('progressDivContainer')
progressDivContainer.style.display = 'none'
}
}, 5000)
}
consoleLog('VSS.require function')
try {
Expand All @@ -49,11 +57,8 @@
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")

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

// Get the header element
Expand All @@ -71,11 +76,13 @@
codeAlertCount: $(progressDivContainer).find('.codeAlertCount')[0]
}
const projects = await getProjects(VSS, Service, RestClient)
// filter the projects to only the current project
const currentProject = projects.filter(p => p.id === projectId)
consoleLog(`Found [${projects?.length}] projects`)
loadAllAlertsFromAllRepos(VSS, Service, GitWebApi, organization, projects, progressDiv, redrawCharts)
loadAllAlertsFromAllRepos(VSS, Service, GitWebApi, organization, currentProject, progressDiv, redrawCharts)

// create the empty charts
await createCharts({chartService, projectName, organization})
await createCharts({chartService, projectName: project.name, organization})

consoleLog('returning WidgetStatusHelper.Success()')
}
Expand All @@ -90,7 +97,11 @@
<div class="widget" id="progressDivContainer">
<div>
<div class="float" style="font-weight:600;font-size:larger;margin-right:7px;">Loading data</div>
<div class="loadingTitle float float-break"></div>
<div class="loadingTitle float margin-right:7px;"></div>
<div class="float float-break" style="margin-right:7px;">
<input type="checkbox" id="debugInfoCheckbox" name="debugInfoCheckbox" value="debugInfoCheckbox" unchecked>
<label for="debugInfoCheckbox">Show debugging</label>
</div>
</div>
<div>
<div id="debugInfo" class="float float-break" date-startTime=""></div>
Expand Down Expand Up @@ -123,11 +134,13 @@ <h3>Code</h3>
</div>
</div>

<div class="widget float">
<div class="widget trendlineWidget float float-break">
<h2 class="ghazdoTitle" id="TitleOverallTrend">Overall alert trend</h2>
<div id="ChartContainerOverallTrend"></div>
</div>

<div style="clear: both;"></div>

<div class="widget float">
<h2 class="ghazdoTitle" id="TitleOverallDependencyPie">Overall dependencies alert levels</h2>
<div id="ChartContainerOverallDependencyPie"></div>
Expand All @@ -151,12 +164,16 @@ <h2 class="ghazdoTitle" id="TitleOverallCodeScanningPie">Overall code scanning a
padding: 5px 5px 5px 5px;
}

.trendlineWidget {
width: 650px;
}

.region-headerBreadcrumb {
display: none;
}

#progressDivContainer {
width: 435px;
width: 440px;
}

.float {
Expand Down
13 changes: 10 additions & 3 deletions widgets/widgets/hub/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ async function createCharts({chartService, projectName, organization}) {
alertType: 1,
repo: "All repos",
repoId: "-1",
widgetWidth: 4,
containerName: "ChartContainerOverallTrend",
titleName: "TitleOverallTrend"
}
Expand Down Expand Up @@ -79,12 +80,16 @@ async function createHubChart({chartService, organization, projectName, data}) {
columnSpan: 2
}

if (data.widgetWidth) {
chartSize.columnSpan = data.widgetWidth;
}

switch (chartType) {
case "2":
try {
const alertType = GetAlertTypeFromValue(alertTypeConfig)
if (titleElement) {
titleElement.textContent = `${alertType.display} Alerts by Severity`
titleElement.textContent = `${alertType.display} alerts by severity`
}
await renderPieChart(organization, projectName, repoId, containerElement, chartService, alertType, chartSize)
}
Expand All @@ -95,9 +100,11 @@ async function createHubChart({chartService, organization, projectName, data}) {
default:
try {
if (titleElement) {
titleElement.textContent = `Advanced Security Alerts Trend`
titleElement.textContent = `Advanced Security alerts trend for ${projectName}`
}
await renderTrendLine(organization, projectName, repoId, containerElement, chartService, chartSize)
const daysToGoBack = 26 * 7 // look back half a year in chunks that count up to today
const summaryBucket = 7
await renderTrendLine(organization, projectName, repoId, containerElement, chartService, chartSize, daysToGoBack, summaryBucket)
}
catch (err) {
consoleLog(`Error loading the alerts trend: ${err}`)
Expand Down