Skip to content

Commit

Permalink
fix(parseable): add support for critical vulns and more resolves on u…
Browse files Browse the repository at this point in the history
…pdate/install action (#28)

Fixes: https://npm.community/t/1419
  • Loading branch information
larsgw authored and zkat committed Aug 15, 2018
1 parent 978a82f commit 5e27893
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 18 deletions.
23 changes: 13 additions & 10 deletions reporters/parseable.js
Expand Up @@ -11,6 +11,7 @@ const report = function (data, options) {

const actions = function (data, config) {
let accumulator = {
critical: '',
high: '',
moderate: '',
low: ''
Expand All @@ -25,16 +26,18 @@ const report = function (data, options) {
l.recommendation = recommendation.cmd
l.breaking = recommendation.isBreaking ? 'Y' : 'N'

// TODO: Verify: The advisory seems to repeat and be the same for all the 'resolves'. Is it true?
const advisory = data.advisories[action.resolves[0].id]
l.sevLevel = advisory.severity
l.severity = advisory.title
l.package = advisory.module_name
l.moreInfo = `https://nodesecurity.io/advisories/${advisory.id}`
l.path = action.resolves[0].path
action.resolves.forEach((resolution) => {
const advisory = data.advisories[resolution.id]

accumulator[advisory.severity] += [action.action, l.package, l.sevLevel, l.recommendation, l.severity, l.moreInfo, l.path, l.breaking]
.join('\t') + '\n'
l.sevLevel = advisory.severity
l.severity = advisory.title
l.package = advisory.module_name
l.moreInfo = `https://nodesecurity.io/advisories/${advisory.id}`
l.path = resolution.path

accumulator[advisory.severity] += [action.action, l.package, l.sevLevel, l.recommendation, l.severity, l.moreInfo, l.path, l.breaking]
.join('\t') + '\n'
}) // forEach resolves
}

if (action.action === 'review') {
Expand All @@ -53,7 +56,7 @@ const report = function (data, options) {
} // is review
}) // forEach actions
}
return accumulator['high'] + accumulator['moderate'] + accumulator['low']
return accumulator['critical'] + accumulator['high'] + accumulator['moderate'] + accumulator['low']
}

const exitCode = function (metadata) {
Expand Down
127 changes: 127 additions & 0 deletions test/fixtures/some-same-action.json
@@ -0,0 +1,127 @@
{
"actions": [
{
"action": "install",
"module": "mocha-jenkins-reporter",
"target": "0.3.12",
"isMajor": false,
"resolves": [
{
"id": 534,
"path": "mocha-jenkins-reporter>mocha>debug",
"dev": true,
"optional": false,
"bundled": false
},
{
"id": 146,
"path": "mocha-jenkins-reporter>mocha>growl",
"dev": true,
"optional": false,
"bundled": false
}
]
}
],
"advisories": {
"146": {
"findings": [
{
"version": "1.9.2",
"paths": [
"mocha-jenkins-reporter>mocha>growl"
],
"dev": true,
"optional": false,
"bundled": false
}
],
"id": 146,
"created": "2016-09-06T12:49:40.000Z",
"updated": "2018-03-02T21:07:28.071Z",
"deleted": null,
"title": "Command Injection",
"found_by": {
"name": "Cristian-Alexandru Staicu"
},
"reported_by": {
"name": "Cristian-Alexandru Staicu"
},
"module_name": "growl",
"cves": [
"CVE-2017-16042"
],
"vulnerable_versions": "<1.10.2",
"patched_versions": ">=1.10.2",
"overview": "Affected versions of `growl` do not properly sanitize input prior to passing it into a shell command, allowing for arbitrary command execution.",
"recommendation": "Update to version 1.10.2 or later.",
"references": "[Issue #60](https://github.com/tj/node-growl/issues/60)\n[PR #61](https://github.com/tj/node-growl/pull/61)",
"access": "public",
"severity": "critical",
"cwe": "CWE-94",
"metadata": {
"module_type": "CLI.Library",
"exploitability": 5,
"affected_components": ""
},
"url": "https://nodesecurity.io/advisories/146"
},
"534": {
"findings": [
{
"version": "2.6.8",
"paths": [
"mocha-jenkins-reporter>mocha>debug"
],
"dev": true,
"optional": false,
"bundled": false
}
],
"id": 534,
"created": "2017-09-25T18:55:55.956Z",
"updated": "2018-05-16T19:37:43.686Z",
"deleted": null,
"title": "Regular Expression Denial of Service",
"found_by": {
"name": "Cristian-Alexandru Staicu"
},
"reported_by": {
"name": "Cristian-Alexandru Staicu"
},
"module_name": "debug",
"cves": [
"CVE-2017-16137"
],
"vulnerable_versions": "<= 2.6.8 || >= 3.0.0 <= 3.0.1",
"patched_versions": ">= 2.6.9 < 3.0.0 || >= 3.1.0",
"overview": "Affected versions of `debug` are vulnerable to regular expression denial of service when untrusted user input is passed into the `o` formatter. \n\nAs it takes 50,000 characters to block the event loop for 2 seconds, this issue is a low severity issue.",
"recommendation": "Version 2.x.x: Update to version 2.6.9 or later.\nVersion 3.x.x: Update to version 3.1.0 or later.\n",
"references": "- [Issue #501](https://github.com/visionmedia/debug/issues/501)\n- [PR #504](https://github.com/visionmedia/debug/pull/504)",
"access": "public",
"severity": "low",
"cwe": "CWE-400",
"metadata": {
"module_type": "",
"exploitability": 5,
"affected_components": ""
},
"url": "https://nodesecurity.io/advisories/534"
}
},
"muted": [],
"metadata": {
"vulnerabilities": {
"info": 0,
"low": 1,
"moderate": 0,
"high": 0,
"critical": 1
},
"dependencies": 0,
"devDependencies": 43,
"optionalDependencies": 0,
"totalDependencies": 43
},
"runId": "ab9f276f-15b6-4034-a7a2-f0af6d4420f3"
}
78 changes: 78 additions & 0 deletions test/fixtures/some-vulns-critical.json
@@ -0,0 +1,78 @@
{
"actions": [
{
"action": "install",
"module": "mocha-jenkins-reporter",
"target": "0.3.12",
"isMajor": false,
"resolves": [
{
"id": 146,
"path": "mocha-jenkins-reporter>mocha>growl",
"dev": true,
"optional": false,
"bundled": false
}
]
}
],
"advisories": {
"146": {
"findings": [
{
"version": "1.9.2",
"paths": [
"mocha-jenkins-reporter>mocha>growl"
],
"dev": true,
"optional": false,
"bundled": false
}
],
"id": 146,
"created": "2016-09-06T12:49:40.000Z",
"updated": "2018-03-02T21:07:28.071Z",
"deleted": null,
"title": "Command Injection",
"found_by": {
"name": "Cristian-Alexandru Staicu"
},
"reported_by": {
"name": "Cristian-Alexandru Staicu"
},
"module_name": "growl",
"cves": [
"CVE-2017-16042"
],
"vulnerable_versions": "<1.10.2",
"patched_versions": ">=1.10.2",
"overview": "Affected versions of `growl` do not properly sanitize input prior to passing it into a shell command, allowing for arbitrary command execution.",
"recommendation": "Update to version 1.10.2 or later.",
"references": "[Issue #60](https://github.com/tj/node-growl/issues/60)\n[PR #61](https://github.com/tj/node-growl/pull/61)",
"access": "public",
"severity": "critical",
"cwe": "CWE-94",
"metadata": {
"module_type": "CLI.Library",
"exploitability": 5,
"affected_components": ""
},
"url": "https://nodesecurity.io/advisories/146"
}
},
"muted": [],
"metadata": {
"vulnerabilities": {
"info": 0,
"low": 1,
"moderate": 0,
"high": 0,
"critical": 1
},
"dependencies": 0,
"devDependencies": 43,
"optionalDependencies": 0,
"totalDependencies": 43
},
"runId": "ab9f276f-15b6-4034-a7a2-f0af6d4420f3"
}
31 changes: 23 additions & 8 deletions test/parseable-report-test.js
Expand Up @@ -6,29 +6,29 @@ const Keyfob = require('keyfob')

const fixtures = Keyfob.load({ path: 'test/fixtures', fn: require })

tap.test('it generates a detail report with no vulns', function (t) {
tap.test('it generates a parseable report with no vulns', function (t) {
return Report(fixtures['no-vulns'], {reporter: 'parseable'}).then((report) => {
t.match(report.exitCode, 0, 'successful exit code')
t.equal(report.report.length, 0, 'no vulns reported')
})
})

tap.test('it generates a detail report with one vuln (update action)', function (t) {
tap.test('it generates a parseable report with one vuln (update action)', function (t) {
return Report(fixtures['one-vuln-one-pkg'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /\tnpm update tough-cookie --depth 6/, 'recommends update command with --depth')
})
})

tap.test('it generates a detail report with one vuln (update action)', function (t) {
tap.test('it generates a parseable report with one vuln (update action)', function (t) {
return Report(fixtures['one-vuln'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /^update/)
t.match(report.report, /npm update tough-cookie --depth 6/)
})
})

tap.test('it generates a detail report with one vuln (install action)', function (t) {
tap.test('it generates a parseable report with one vuln (install action)', function (t) {
return Report(fixtures['one-vuln-install'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /^install/)
Expand All @@ -50,22 +50,22 @@ tap.test('it adds a message if a dep isMajor (multiple vulns)', function (t) {
})
})

tap.test('it generates a detail report with one vuln (install dev dep)', function (t) {
tap.test('it generates a parseable report with one vuln (install dev dep)', function (t) {
return Report(fixtures['one-vuln-dev'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /npm install --save-dev knex@3.0.0/)
})
})

tap.test('it generates a detail report with one vuln (review dev dep)', function (t) {
tap.test('it generates a parseable report with one vuln (review dev dep)', function (t) {
return Report(fixtures['one-vuln-dev-review'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /review\t/, 'expects manual review')
t.match(report.report, /\tknex/)
})
})

tap.test('it generates a detail report with some vulns', function (t) {
tap.test('it generates a parseable report with some vulns', function (t) {
return Report(fixtures['some-vulns'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /review\t/, 'expects manual review')
Expand All @@ -76,9 +76,24 @@ tap.test('it generates a detail report with some vulns', function (t) {
})
})

tap.test('it generates a detail report with review vulns', function (t) {
tap.test('it generates a parseable report with review vulns', function (t) {
return Report(fixtures['update-review'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /review\t/, 'expects manual review')
})
})

tap.test('it generates a parseable report with critical vulns', function (t) {
return Report(fixtures['some-vulns-critical'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /\tcritical/)
})
})

tap.test('it generates a parseable report with multiple resolves on the same update/install action', function (t) {
return Report(fixtures['some-same-action'], {reporter: 'parseable'}).then((report) => {
t.equal(report.exitCode, 1, 'non-zero exit code')
t.match(report.report, /\tcritical/)
t.match(report.report, /\tlow/)
})
})

0 comments on commit 5e27893

Please sign in to comment.