-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from nwestfall/github_action
GitHub action
- Loading branch information
Showing
7 changed files
with
337 additions
and
517 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
const Netsparker = require('./netsparker'); | ||
const core = require('@actions/core') | ||
const githubEvent = require(process.env.GITHUB_EVENT_PATH) | ||
|
||
async function exec () { | ||
try | ||
{ | ||
var config = parseConfig(); | ||
netsparker = new Netsparker(config.userid, config.apitoken, config.profilename, config.targetsite); | ||
const scanId = await netsparker.scan(); | ||
if(config.report | ||
|| (config.criticalthreshold || config.highthreshold || config.mediumthreshold)) { | ||
await netsparker.waitForScanToComplete(scanId); | ||
const scanResults = await netsparker.scanResults(scanId); | ||
core.setOutput('scanresults', scanResults); | ||
const scanReport = await netsparker.scanReport(scanId, 'Vulnerabilities', 'Json'); | ||
core.setOutput('scanreport', scanReport); | ||
if(config.report) { | ||
if(config.junit) { | ||
await this.netsparker.createJunitTestReport(scanResults, config.junit); | ||
} else { | ||
console.table(scanResults); | ||
} | ||
} | ||
if(config.criticalthreshold || config.highthreshold || config.mediumthreshold) { | ||
var criticalCount = 0; | ||
var highCount = 0; | ||
var mediumCount = 0; | ||
for(var i = 0; i < scanReport.Vulnerabilities.length; i++) { | ||
var v = scanReport.Vulnerabilities[i]; | ||
switch(v.Severity) { | ||
case "Critical": | ||
criticalCount++; | ||
break; | ||
case "High": | ||
highCount++; | ||
break; | ||
case "Medium": | ||
mediumCount++; | ||
break; | ||
} | ||
} | ||
|
||
var thresholdReached = false; | ||
if(config.criticalthreshold) { | ||
if(criticalCount > parseInt(config.criticalthreshold)) { | ||
thresholdReached = true; | ||
console.error(`Critical count exceeds threshold (${criticalCount}).`); | ||
} | ||
} | ||
if(config.highthreshold) { | ||
if(highCount > parseInt(config.highthreshold)) { | ||
thresholdReached = true; | ||
console.error(`High count exceeds threshold (${highCount}).`); | ||
} | ||
} | ||
if(config.mediumthreshold) { | ||
if(mediumCount > parseInt(config.mediumthreshold)) { | ||
thresholdReached = true; | ||
console.error(`Medium count exceeds threshold (${mediumCount}).`) | ||
} | ||
} | ||
|
||
if(thresholdReached) { | ||
throw new Error("One or more thresholds where reached. Please see report in Netsparker"); | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
console.error(error) | ||
process.exit(1) | ||
} | ||
} | ||
|
||
function parseConfig () { | ||
return { | ||
userid: core.getInput('userid'), | ||
apitoken: core.getInput('apitoken'), | ||
profilename: core.getInput('profilename'), | ||
targetsite: core.getInput('targetsite'), | ||
report: core.getInput('report'), | ||
junit: core.getInput('junit'), | ||
criticalthreshold: core.getInput('criticalthreshold'), | ||
highthreshold: core.getInput('highthreshold'), | ||
mediumthreshold: core.getInput('mediumthreshold') | ||
} | ||
} | ||
|
||
exec() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
name: 'Netsparker Scan Runner' | ||
description: 'Run Netsparker Scans and get back test results' | ||
author: 'nwestfall' | ||
inputs: | ||
userid: | ||
description: 'The user id from your Netsparker Account' | ||
required: true | ||
apitoken: | ||
description: 'The api token from your Netsparker Account' | ||
required: true | ||
profilename: | ||
description: 'The profile name saved in your Netsparker Account' | ||
required: true | ||
targetsite: | ||
description: 'The target url you want to run against' | ||
required: true | ||
report: | ||
description: 'If you want to wait around for the report (true) or to fire and forget (false)' | ||
required: false | ||
default: 'true' | ||
junit: | ||
description: 'If you want to generate a junit report, enter the file name and location here' | ||
required: false | ||
criticalthreshold: | ||
description: 'Critical Severity Threshold' | ||
required: false | ||
default: '0' | ||
highthreshold: | ||
description: 'High Severity Threshold' | ||
required: false | ||
default: '0' | ||
mediumthreshold: | ||
description: 'Medium Severity Threshold' | ||
required: false | ||
default: '0' | ||
outputs: | ||
scanresults: | ||
description: 'Scan results from Netsparker (blank if `report` is false)' | ||
scanreport: | ||
description: 'Scan report from Netsparker (blank if `report` is false)' | ||
runs: | ||
using: 'node12' | ||
main: 'dist/index.js' | ||
branding: | ||
icon: 'shield' | ||
color: 'orange' |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
const jUnitBuilder = require('junit-report-builder'); | ||
const fetch = require('node-fetch'); | ||
const header = require('basic-auth-header'); | ||
const sleep = require('sleep-promise'); | ||
|
||
class Netsparker { | ||
constructor(userid, apitoken, profilename, targetsite) { | ||
this.userid = userid; | ||
this.apitoken = apitoken; | ||
this.profilename = profilename; | ||
this.targetsite = targetsite; | ||
} | ||
|
||
async scan() { | ||
const response = await fetch('https://www.netsparkercloud.com/api/1.0/scans/newwithprofile', { | ||
method: 'POST', | ||
body: `{ "ProfileName": "${this.profilename}", "TargetUri": "${this.targetsite}" }`, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Accept': 'application/json', | ||
'Authorization': header(this.userid, this.apitoken) | ||
} | ||
}); | ||
const body = await response.text(); | ||
if(!response.ok) { | ||
throw new Error(`${response.statusText} - ${body}`); | ||
} | ||
const scanId = JSON.parse(body).Id; | ||
return scanId; | ||
} | ||
|
||
async scanStatus(scanId) { | ||
const response = await fetch(`https://www.netsparkercloud.com/api/1.0/scans/status/${scanId}`, { | ||
method: 'GET', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Authorization': header(this.userid, this.apitoken) | ||
} | ||
}); | ||
|
||
if(!response.ok) { | ||
throw new Error(response.statusText); | ||
} | ||
|
||
const body = await response.text(); | ||
const result = JSON.parse(body); | ||
return result; | ||
} | ||
|
||
async waitForScanToComplete(scanId) { | ||
var complete = false; | ||
do | ||
{ | ||
const scanStatusResult = await this.scanStatus(scanId); | ||
if(scanStatusResult.State == "Complete") | ||
complete = true; | ||
else { | ||
if(scanStatusResult.EstimatedLaunchTime == null) | ||
console.log(`Scan running - ${scanStatusResult.CompletedSteps}/${scanStatusResult.EstimatedSteps} complete`); | ||
else | ||
console.log(`Scan estimated start time - ${scanStatusResult.EstimatedLaunchTime}`); | ||
await sleep(5000); | ||
} | ||
} while(!complete); | ||
} | ||
|
||
async scanResults(scanId) { | ||
const response = await fetch(`https://www.netsparkercloud.com/api/1.0/scans/result/${scanId}`, { | ||
method: 'GET', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Authorization': header(this.userid, this.apitoken) | ||
} | ||
}); | ||
|
||
if(!response.ok) { | ||
throw new Error(response.statusText); | ||
} | ||
|
||
const body = await response.text(); | ||
const results = JSON.parse(body); | ||
return results; | ||
} | ||
|
||
async scanReport(scanId, type, format) { | ||
const response = await fetch(`https://www.netsparkercloud.com/api/1.0/scans/report/?excludeResponseData=true&format=${format}&id=${scanId}&type=${type}`, { | ||
method: 'GET', | ||
headers: { | ||
'Authorization': header(this.userid, this.apitoken) | ||
} | ||
}); | ||
|
||
if(!response.ok) { | ||
throw new Error(response.statusText); | ||
} | ||
|
||
const body = await response.text(); | ||
const results = JSON.parse(body); | ||
return results; | ||
} | ||
|
||
createJunitTestReport(scanResults, junitFile) { | ||
const suite = jUnitBuilder.testSuite().name('NetsparkerSuite'); | ||
for(var i = 0; i < scanResults.length; i++) { | ||
const result = scanResults[i]; | ||
suite.testCase() | ||
.className(result.Type) | ||
.name(result.Title) | ||
.standardOutput(result.IssueUrl) | ||
.failure(); | ||
} | ||
jUnitBuilder.writeTo(junitFile); | ||
} | ||
} | ||
|
||
module.exports = Netsparker |
Oops, something went wrong.