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

Ignore unfixed issues for exit code #133

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ provided directly.

## options

| option | values | default | description |
| :--- | :--- | :--- |:--- |
| reporter | `install`, `detail`, `json`, `quiet` | `install` | specify which output format you want to use |
| chalk   | `Chalk` instance  | required  | a Chalk instance to use for colorizing strings. use `new chalk.Instance({ level: 0 })` for no colors |
| unicode  | `true`, `false`                  | `true` | indicates if unicode characters should be used|
| indent   | Number or String                | `2` | indentation for `'json'` report|
| auditLevel | 'info', 'low', 'moderate', 'high', 'critical', 'none' | `low` (ie, exit 0 if only `info` advisories are found) | level of vulnerability that will trigger a non-zero exit code (set to 'none' to always exit with a 0 status code) |
| option | values | default | description |
|:--------------|:------------------------------------------------------|:-------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
| reporter | `install`, `detail`, `json`, `quiet` | `install` | specify which output format you want to use |
| chalk | `Chalk` instance | required | a Chalk instance to use for colorizing strings. use `new chalk.Instance({ level: 0 })` for no colors |
| unicode | `true`, `false` | `true` | indicates if unicode characters should be used |
| indent | Number or String | `2` | indentation for `'json'` report |
| auditLevel | 'info', 'low', 'moderate', 'high', 'critical', 'none' | `low` (ie, exit 0 if only `info` advisories are found) | level of vulnerability that will trigger a non-zero exit code (set to 'none' to always exit with a 0 status code) |
| ignoreUnfixed | `true`, `false` | `false` | specify if vulnerabilities without a fix should be ignored to generate a non-zero exit code (useful for CI) |
27 changes: 24 additions & 3 deletions lib/exit-code.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// return 1 if any vulns in the set are at or above the specified severity
/**
* Determine the exit code based on the severity level and if a fix is available
* Return 1 if any vulnerabilities in the set are at or above the specified severity
* and, if ignoreUnfixed is true, have a fix available
*/
const severities = new Map(Object.entries([
'info',
'low',
Expand All @@ -8,7 +12,24 @@ const severities = new Map(Object.entries([
'none',
]).map(s => s.reverse()))

module.exports = (data, level) =>
Object.entries(data.metadata.vulnerabilities)
/**
* Get number of vulnerabilities with fix available grouped by severity
* @param data
* @returns {Object}
*/
const getVulnerabilitiesWithFix = (data) => {
return Object.entries(data.vulnerabilities)
.filter(([, vuln]) => vuln.fixAvailable)
.reduce((result, [, vuln]) => {
result[vuln.severity] = (result[vuln.severity] || 0) + 1
return result
}, {})
}

module.exports = (data, level, ignoreUnfixed = false) => {
const vulnerabilities =
ignoreUnfixed ? getVulnerabilitiesWithFix(data) : data.metadata.vulnerabilities
return Object.entries(vulnerabilities)
.some(([sev, count]) => count > 0 && severities.has(sev) &&
severities.get(sev) >= severities.get(level)) ? 1 : 0
}
3 changes: 2 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = Object.assign((data, options = {}) => {
chalk,
unicode = true,
indent = 2,
ignoreUnfixed = false,
} = options

// CLI defaults this to `null` so the defaulting method above doesn't work
Expand All @@ -36,6 +37,6 @@ module.exports = Object.assign((data, options = {}) => {

return {
report: reporters[reporter](data, { chalk, unicode, indent }),
exitCode: exitCode(data, auditLevel),
exitCode: exitCode(data, auditLevel, ignoreUnfixed),
}
}, { reporters })
41 changes: 41 additions & 0 deletions test/exit-code.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const t = require('tap')
const exitCode = require('../lib/exit-code.js')
const fixture = require('./fixtures/index.js')

t.equal(exitCode({
metadata: {
Expand Down Expand Up @@ -44,3 +45,43 @@ t.equal(exitCode({
},
},
}, 'low'), 1)

t.test('ignoreUnfixed with no vulnerabilities', t => {
const data = fixture('no-vulns')
t.equal(
exitCode(data, 'low', true),
0,
'return 0, no vulnerabilities and ignoreUnfixed is true'
)
t.end()
})

t.test('ignoreUnfixed and one vulnerability with fix', t => {
const data = fixture('one-vuln')
t.equal(
exitCode(data, 'low', true),
1,
'return 1, one vulnerability with fix and ignoreUnfixed is true'
)
t.end()
})

t.test('ignoreUnfixed with vulnerabilities without fix', t => {
const data = fixture('not-in-registry')
t.equal(
exitCode(data, 'low', true),
0,
'return 0, vulnerabilities have no fix and ignoreUnfixed is true'
)
t.end()
})

t.test('disable ignoreUnfixed with vulnerabilities without fix', t => {
const data = fixture('not-in-registry')
t.equal(
exitCode(data, 'low', false),
1,
'return 1, vulnerabilities have no fix and ignoreUnfixed is false'
)
t.end()
})